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
msimondsmsimonds 

Hitting Governor Limit and do not understand why

In the past I was hitting the govenor limit of 21 quieres and have been able to work through that, but after I retooled my trigger and class, I am getting the same error.

 

I have this trigger:

 

trigger UpdateOppChatterFeed on OpportunityLineItem (after update) 
{
      integer size = Trigger.new.size();

      for(integer i = 0; i < size; i++)
      {
            Chatter_Feed cf = new Chatter_Feed();
            cf.updateChatter(Trigger.new[i]);
      }
}

 

 

which calls this class:

 

 

public with sharing class Chatter_Feed 
{

	public void updateChatter(OpportunityLineItem  oli)
	{
            List<FeedPost> posts = new List<FeedPost>();
            List<Opportunity> oppList = new List<Opportunity>();
            List<Opportunity> OppsToBeUpdated = new List<Opportunity>();
            
            oppList = [select Id
            		   from Opportunity 
            		   where id = : oli.OpportunityId];
            		   
						
            id idToUpdate;   
            for(Opportunity opp : oppList)
            {
                  idToUpdate = opp.Id;			
            }
	    System.debug('MIKES OPP ID IS '  + idToUpdate);
            //update OppsToBeUpdated;
            List<OpportunityFeed> opportunityFeedPosts = [SELECT Id, Type, FeedPost.Body
                                                            From OpportunityFeed
                                                            Where ParentID = :idToUpdate
                                                            ORDER BY CreatedDate DESC];
                                                            
                                                            
            String bodyText = 'This is the body ';                                                
            FeedPost opportunityPost = new Feedpost();
            opportunityPost.Type = 'LinkPost';
            opportunityPost.Title = 'THIS IS THE TITLE WILL';
            opportunityPost.Body = bodyText;
            String id = String.valueOf(oli.id).substring(0,15);
            opportunityPost.LinkURL = 'https://cs1.salesforce.com/'+id;
            opportunityPost.ParentID = idToUpdate;
            posts.add(opportunityPost);
            	
            //}                                        
                                                            
            insert posts;                                              

      }
 
  
}

 

 

The only for() loop that I have is in the class and does not query any sObjects.

 

This trigger/class works when I edit 1 OpportunityLineItem, but our Org has an option to edit all and in some cases, there are more than 21 line items on an Opportunity.

 

Why is this failing?  Can someone please give me some guidance here?

 

 

I would appreciate any feedback

 

Thanks!!

 

~Mike

Best Answer chosen by Admin (Salesforce Developers) 
msimondsmsimonds

I was able to re-write the trigger and posted the resolution on my blog:

 

http://www.mikesimonds.com/blogs/mike/25-saleforce-apex-chatter-trigger-tutorial.html

All Answers

DTCFDTCF

You should not have the queries inside the loop. What is happening is that since you are calling updateChatter as many times as there are OpportunityLineItems to be updated, it is executing the two SOQL queries that many times, including the insert posts part.

 

In this case, doing the processing inside a separate class will probably needlessly complicated things.

 

In the main trigger, try something like this:

 

 

List<Id> oppIdsToBeUpdated = new List<Id>();

for (OpportunityLineItem oli: Trigger.new) {
     oppIdsToBeUpdated.add(oli.opportunityId);


}

List<OpportunityFeed> opportunityFeedPosts = [select id,type,feedpost.body from OpportunityFeed where parentId in :oppIdsToBeUpdated];

// You don't use the above anywhere else? Comment it out?

// The rest should be about the same, except you can iterate on the opportunity ID assuming you are doing it in a single trigger rather than its own class

List<FeedPost> posts = new List<FeedPost>();

for (Id oppId : oppIdsToBeUpdated) {

   FeedPost opportunityPost = new Feedpost();
       // Create individual post

     opportunityPost.parentId = oppId;
     posts.add(opportunityPost);

}

insert posts;

 

I could be missing something since I'm not sure if I'm able to follow all of your logic. Hope that helps some at least!

 

msimondsmsimonds

Thank you very much for responding and I will try your solution out and let you know if it works. I appreciate you taking the time to do this.

 

 

~Mike

msimondsmsimonds

I tried your trigger and it works, I appreciate it, but running into one snag.

 

In the lower part of the trigger

 

 

for (Id oppId : oppIdsToBeUpdated) 
	{	
	   FeedPost opportunityPost = new Feedpost();
	   // Create individual post	
	   opportunityPost.parentId = oppId;
	   String bodyText = 'This is the body ';                                                
	   opportunityPost.Type = 'LinkPost';
	   opportunityPost.Title = 'THIS IS THE TITLE WILL';
	   opportunityPost.Body = bodyText;
	   //String id = String.valueOf(oli.Id).substring(0,15);
	   opportunityPost.LinkURL = 'https://cs1.salesforce.com/';
	   posts.add(opportunityPost);
	   
	}

 

 

you see the commented out String Id variable that we are creating. it is supposed to create a link that has the ID of the opportunity line item (oli.id) but in the upper for loop you are just adding the opporunityId  to the list > oppIdsToBeUpdated

 

Eclipse is telling me that oli.Id is not defined and I agree, but how can I define it when you are just adding the OpporunityId to that list

 

Thanks for all your help!!

 

~Mike

msimondsmsimonds

I wanted to add the whole trigger in case it would make more sense

 

 

trigger new_chatter_update_trigger on OpportunityLineItem (after update) 
{

	List<Id> oppIdsToBeUpdated = new List<Id>();
	
	
	for (OpportunityLineItem oli: Trigger.new) 
	{
	     oppIdsToBeUpdated.add(oli.opportunityId);
	     	
	}
	
	
	
	List<OpportunityFeed> opportunityFeedPosts = [select id,type,feedpost.body from OpportunityFeed where parentId in :oppIdsToBeUpdated];

	// You don't use the above anywhere else? Comment it out?

	// The rest should be about the same, except you can iterate on the opportunity ID assuming you are doing it in a single trigger rather than its own class

	List<FeedPost> posts = new List<FeedPost>();

	for (Id oppId : oppIdsToBeUpdated) 
	{	
	   FeedPost opportunityPost = new Feedpost();
	   // Create individual post	
	   opportunityPost.parentId = oppId;
	   String bodyText = 'This is the body ';                                                
	   opportunityPost.Type = 'LinkPost';
	   opportunityPost.Title = 'THIS IS THE TITLE WILL';
	   opportunityPost.Body = bodyText;
	   //String id = String.valueOf(oli.Id).substring(0,15);
	   opportunityPost.LinkURL = 'https://cs1.salesforce.com/';
	   posts.add(opportunityPost);
	   
	}
	
	insert posts;







}

 

 

DTCFDTCF

Unless I'm missing something, I think you might be able to do it just in a single loop!

 

	List<FeedPost> posts = new List<FeedPost>();

for (OpportunityLineItem oli : Trigger.new)
{
FeedPost opportunityPost = new Feedpost();
// Create individual post
opportunityPost.parentId = oli.opportunityId;
String bodyText = 'This is the body ';
opportunityPost.Type = 'LinkPost';
opportunityPost.Title = 'THIS IS THE TITLE WILL';
opportunityPost.Body = bodyText;
//String id = String.valueOf(oli.Id).substring(0,15);
opportunityPost.LinkURL = 'https://cs1.salesforce.com/';
posts.add(opportunityPost);

}

insert posts;

 

 

msimondsmsimonds

:smileywink: that did it, I did not even think about that.

 

Listen there are a couple more things that I need to add

 

We only want to update the Chatter post if it meets some business logic.  Your fix has pointed us in the right direction and I totally appreciate that very much.

 

If you look at the original trigger we were working on it was also causing issues with the govenor limit

 

 

 

trigger LineItemToOpportunityFeedUpdate on OpportunityLineItem (after update) {
    List<FeedPost> posts = new List<FeedPost>();
    Set<Id> pbeIds = new Set<Id>();
    
    for(OpportunityLineItem newOLI : Trigger.new) {
        OpportunityLineItem oldOLI = Trigger.oldMap.get (newOLI.id);
        //System.debug(' WILL IS A FRIEND OF MINE - New: '+newOLI.Part_Outcome__c+' Old: '+oldOLI.Part_Outcome__c);
        
        if(newOLI.Part_Outcome__c != oldOLI.Part_Outcome__c){ //&& (newOLI.Part_Outcome__c <> 'Open')) {
            
            List<OpportunityFeed> opportunityFeedPosts = [SELECT Id, Type, FeedPost.Body
                                                            From OpportunityFeed
                                                            Where ParentID = :newOLI.OpportunityID
                                                            ORDER BY CreatedDate DESC];
           					  //Errors @line 31, character 58 before SELECT            
            pbeIds.add(newOLI.PricebookEntryId);
            Map<Id, PricebookEntry> entries = new Map<Id, PricebookEntry>([select Product2.ProductCode from PricebookEntry where id in :pbeIds]);

            pbeIds.add(newOLI.opportunityid);
            Map<Id, Opportunity> Opp = new Map<Id, Opportunity>([select Account.Name, Account.ID from Opportunity where id in :pbeIds]);
                             //Errors @line 40, character 60 before SELECT                                               
            //String bodyText = 'Got here....'+Opp.get(newOLI.OpportunityId).Account.Name+'';                                                               
            String bodyText = 'has updated the '+entries.get(newOLI.Pricebookentryid).Product2.ProductCode+' from '+oldOLI.Part_Outcome__c+' to '+newOLI.Part_Outcome__c+' at '+opp.get(newOLI.OpportunityId).Account.Name+'';    
            
            if(opportunityFeedPosts.size() == 0 || opportunityFeedPosts[0].FeedPost.Body != bodyText) {
                //System.debug('OpportunityFeed Posts: '+opportunityFeedPosts[0]);

               
                FeedPost opportunityPost = new Feedpost();
                opportunityPost.Type = 'LinkPost';
                opportunityPost.Title = ''+entries.get(newOLI.Pricebookentryid).Product2.ProductCode+' socket details';
                opportunityPost.Body = bodyText;
                String id = String.valueOf(newOLI.Id).substring(0,15);
                opportunityPost.LinkURL = 'https://na2.salesforce.com/'+id;
                opportunityPost.ParentID = newOLI.opportunityid;
                posts.add(opportunityPost);
                System.Debug('Got Here but did not post');
                
                /*@Version 1.3 - removed because I could not get it to display one feedpost with 2 links in the Opportunity Post Title
                FeedPost opportunityPost2 = new FeedPost ();
                opportunityPost2.Type = 'LinkPost';
                opportunityPost2.Title = 'Account Details';
                opportunityPost2.Body = bodyText;
                String id2 = String.valueOf(opp.get(newOLI.OpportunityId).Account.Id).substring(0,15);
                opportunityPost2.LinkURL = 'https://na2.salesforce.com/'+id2;
                opportunityPost2.ParentID = newOLI.opportunityid;
                posts.add(opportunityPost2);
                system.debug('got here and it should have posted twice');
                */
                }
            }
         
//This section will trigger a feed update to the Opportunity if the Max Potential of any given socket is increased by $1,000,000           
        if(newOLI.Max_Potential__c >= oldOLI.Max_Potential__c + 1000000){
            
            List<OpportunityFeed> opportunityFeedPosts = [Select Id, Type, FeedPost.Body
                                                            From OpportunityFeed
                                                            Where ParentId = :newOLI.OpportunityID
                                                            ORDER BY CreatedDate DESC];
        
            //@1.1 Code that will traverse the Pricebookentry ID and pull back the part number. We could not directly access the part number from the opportunitylineitem.productcode field
            pbeIds.add(newOLI.PricebookEntryId);    
            Map<Id, PricebookEntry> entries = new Map<Id, PricebookEntry>([select Product2.ProductCode from PricebookEntry 
                                                                            where id in :pbeIds]);
                                                                            
            //Traverse the opportunity object to return the Account detail and get the account name                                                                
            pbeIds.add(newOLI.opportunityid);
            Map<Id, Opportunity> Opp = new Map<Id, Opportunity>([select Account.Name, Account.ID from Opportunity where id in :pbeIds]);                                                                
            
            //Round the dollar values to whole numbers
            decimal NewWholeMaxPotential = newOLI.Max_Potential__c;
            decimal maxpotentialdifference = newOLI.Max_Potential__c/1.0 - oldOLI.Max_Potential__c/1.0;                                                             
            
            String bodyText = 'has increased the Max Potential of the '+entries.get(newOLI.Pricebookentryid).Product2.ProductCode+' by $'+maxpotentialdifference+' at '+opp.get(newOLI.OpportunityId).Account.Name+'';
                                                                                                
        if(opportunityFeedPosts.size() == 0 || opportunityFeedPosts[0].FeedPost.Body != bodyText) {
                //System.debug('OpportunityFeed Posts: '+opportunityFeedPosts[0]);
                
                FeedPost opportunityPost = new FeedPost ();
                opportunityPost.Type = 'LinkPost';
                opportunityPost.Title = ''+entries.get(newOLI.Pricebookentryid).Product2.ProductCode+' socket details';
                opportunityPost.Body = bodyText;
                String id = String.valueOf(newOLI.id).substring(0,15);
                opportunityPost.LinkURL = 'https://na2.salesforce.com/'+id;
                opportunityPost.ParentID = newOLI.opportunityid;
                posts.add(opportunityPost);
                }    
            }
        /* @Version1.1 - removed per John Hieb begin_of_the_skype_highlighting     end_of_the_skype_highlighting  
        //This section will update the Chatter feed when the PS3 checkbox is marked true
        //if(newOLI.Socket_Strategy_Required__c == true && oldOLI.Socket_Strategy_Required__C == false){
        if(newOLI.Socket_Strategy_Required__c == true && oldOLI.Socket_Strategy_Required__c == false){  
            List<OpportunityFeed> opportunityFeedPosts = [Select Id, Type, FeedPost.Body
                                                            From OpportunityFeed
                                                            Where ParentId = :newOLI.OpportunityID
                                                            ORDER BY CreatedDate DESC];
                                                      
            pbeIds.add(newOLI.PricebookEntryId);

            Map<Id, PricebookEntry> entries = new Map<Id, PricebookEntry>([select Product2.ProductCode from PricebookEntry 
                                                                            where id in :pbeIds]);
            
            
            //OppOwnerFirstName = get.FirstName;
            //OppOwnerLastName = get.LastName;
                                                                            
            String bodyText = ' has required a PS3 for the '+entries.get(newOLI.Pricebookentryid).Product2.ProductCode+'.';                                             
            //String bodyText = ''+opportunity.OwnerId+' has required a PS3 for the '+entries.get(newOLI.Pricebookentryid).Product2.ProductCode+'.';
            
        if(opportunityFeedPosts.size() == 0 || opportunityFeedPosts[0].FeedPost.Body != bodyText) {
                //System.debug('OpportunityFeed Posts: '+opportunityFeedPosts[0]);
                
                FeedPost opportunityPost = new FeedPost ();
                opportunityPost.Type = 'LinkPost';
                opportunityPost.Title = 'Click here for the PS3';
                opportunityPost.Body = bodyText;
                String id = String.valueOf(newOLI.id).substring(0,15);
                opportunityPost.LinkURL = 'https://na2.salesforce.com'+newOLI.PS3_Link__c+'';
                opportunityPost.ParentID = newOLI.opportunityid;
                posts.add(opportunityPost);
                }    
            }*/         
        }
        insert posts;

 As you can see, we want to compare fields and we are trying to put the part name on the chatter feed when someone changes the part_outcome__c.

 

It would say something like:

 

Mike has updated the MAX6008AEUR+T from Open to Won at Wecan

MAX6008AEUR+T socket details (this would be a link)  > OpportunityPost.LinkURL

 

 

Does this make sense

 

Again, I do appreciate your help and guidance 

 

~Mike


msimondsmsimonds

I was able to re-write the trigger and posted the resolution on my blog:

 

http://www.mikesimonds.com/blogs/mike/25-saleforce-apex-chatter-trigger-tutorial.html

This was selected as the best answer