0

After many hours (possibly days!) I'm struggling to understand where the issue is with my code and your help would be much appreciated.

I have a Trigger on the Quote Object, which calls a Apex Class.

The trigger is as shown below and seems to be fine (I've commented out the IsUpdate part to help narrow down the issue. The IsUpdate part of the corresponding apex code/method has been working fine, but the IsInsert part is not working as expected and is effectively the same code with a few amendments. A copy of this class code is shown further below too and you will see that the IsUpdate/SyncQuoteCustomFields() section is commented out for now too to focus on the IsInsert challenge.

trigger QuoteTrigger on Quote (after insert, after update) {

QuoteUpdateCustomFields NewQuote = new QuoteUpdateCustomFields(Trigger.oldMap, Trigger.newMap);

if(Trigger.IsInsert) {


    NewQuote.NewQuoteCustomFields();
}

if(Trigger.IsUpdate) {



    //NewQuote.SyncQuoteCustomFields();
}

By process of elimination the issue seems to be that one of the For loops doesn't drop in. This is the following for loop; (I kept moving the 'OList[0].description = 'working';' code down through the code, until I found that it was no longer being applied with an Insert action. From this I thinking the issue is then relating to the 'List QliList =....' in the code, but I'm then stumped. (Thank you in advance).

   for (Integer i = 0; i < ListSize; i++) {

            OList[0].description = 'working';

//https://developer.salesforce.com/forums/?id=906F0000000BLCKIA4

public class QuoteUpdateCustomFields{


// These variables store Trigger.oldMap and Trigger.newMap
Map<Id, Quote> oldQuotes;
Map<Id, Quote> newQuotes;

// This is the constructor
// A map of the old and new records is expected as inputs
public QuoteUpdateCustomFields( Map<Id, Quote> oldTriggerQuotes, Map<Id, Quote> newTriggerQuotes) {
        oldQuotes = oldTriggerQuotes;
        newQuotes = newTriggerQuotes;
}

/*
public QuoteUpdateCustomFields( Map<Id, Quote> newTriggerQuotes) {
        newQuotes = newTriggerQuotes;
}
*/


//Method 1
public void NewQuoteCustomFields() {

if(Trigger.IsInsert) {
//Quote IDs
Set<ID> ids = Trigger.newMap.keySet();

//Opp IDs shell
Set<Id> OppIds = new Set<Id>();

//Add Opp ID or IDs to 'OppIds' set
for (Quote QuoteInLoop : newQuotes.values()){
    OppIds.add(QuoteInLoop.OpportunityID);
}

//Maps Opp ID or IDs to 'Map_ID_Opp' map (includes other fields such as Name and RecordTypeId
Map<Id,Opportunity> map_ID_Opp = new Map<Id,Opportunity>([Select id, name, RecordTypeId, AccountId from Opportunity where id in: OppIds]);

//used for storing updates for later update outside of loops    
List<OpportunityLineItem> OliObjectToUpdate = new List<OpportunityLineItem>(); // intermediate list
List<QuoteLineItem> QliObjectToUpdate = new List<QuoteLineItem>(); // intermediate list
List<Opportunity> OppObjectToUpdate = new List<Opportunity>(); // intermediate list
List<Quote> QObjectToUpdate = new List<Quote>(); // intermediate list


List<OpportunityLineItem> OliList = 
            [SELECT Id, Manual_Override_Calculated_Sales_Price__c, Opportunity.RecordType.Name FROM OpportunityLineItem WHERE OpportunityId IN :OppIds]; //= :newOpp.Id];
//where Id IN :Trigger.newMap.keySet()];


List<QuoteLineItem> QliList = 
            [SELECT Id, Product_Type__c, Manual_Override_Calculated_Sales_Price__c, Quote.Opportunity.RecordTypeId, Opportunity_Line_Item_ID__c FROM QuoteLineItem WHERE QuoteId IN :ids]; //= :newOpp.Id];
//where Id IN :Trigger.newMap.keySet()];


List<Opportunity> OList = 
            [SELECT Id, Name, RecordType.Name, Description FROM Opportunity WHERE Id IN :OppIds];



For (Quote newQuote : newQuotes.values()) {


    //Quote oldQuote  = oldQuotes.get(newQuote.Id);

    Opportunity Opp =  map_ID_Opp.get(newQuote.OpportunityID);

//OList[0].description = 'working';
}


//trigger on quote object for Sync   

For (Quote newQuote : newQuotes.values()) {


    //Quote oldQuote  = oldQuotes.get(newQuote.Id);

    Opportunity Opp =  map_ID_Opp.get(newQuote.OpportunityID);

    if( String.ValueOf(Opp.RecordTypeId) == '01280000000Hgd8AAC') {

        Integer ListSize = QliList.size();

        for (Integer i = 0; i < ListSize; i++) {

            OList[0].description = 'working';
            //if(newQuote.IsSyncing == True 
              //&& oldQuote.IsSyncing == false) {

               For (Opportunity OppLoop : map_ID_Opp.values()) {

                   Integer ListSize2 = OliList.size();


                   for (Integer j = 0; j < ListSize2; j++) {


                       //
                       if (OliList[j].id == QliList[i].Opportunity_Line_Item_ID__c) {
                         //&& OliList[j].Manual_Override_Calculated_Sales_Price__c == true) {

                         if (OliList[j].Manual_Override_Calculated_Sales_Price__c == true) {


                             QliList[i].Description = String.ValueOf(Opp.Id);
                             QliList[i].Manual_Override_Calculated_Sales_Price__c = true;

                         } 

                         if (OliList[j].Manual_Override_Calculated_Sales_Price__c == false){

                               QliList[i].Description = String.ValueOf(Opp.Id);
                               QliList[i].Manual_Override_Calculated_Sales_Price__c = false;
                         }  


                       }    

                   }

               }




               QliObjectToUpdate.add(QliList[i]);
            //}

        }

    }

update QliObjectToUpdate;

}


update OList[0];
}
}




/*
//Method 2
public void SyncQuoteCustomFields() {

if(Trigger.IsUpdate) {
//Quote IDs
Set<ID> ids = Trigger.newMap.keySet();

//Opp IDs shell
Set<Id> OppIds = new Set<Id>();

//Add Opp ID or IDs to 'OppIds' set
for (Quote QuoteInLoop : newQuotes.values()){
    OppIds.add(QuoteInLoop.OpportunityID);
}

//Maps Opp ID or IDs to 'Map_ID_Opp' map (includes other fields such as Name and RecordTypeId
Map<Id,Opportunity> map_ID_Opp = new Map<Id,Opportunity>([Select id, name, RecordTypeId, AccountId from Opportunity where id in: OppIds]);

//used for storing updates for later update outside of loops    
List<OpportunityLineItem> OliObjectToUpdate = new List<OpportunityLineItem>(); // intermediate list
List<QuoteLineItem> QliObjectToUpdate = new List<QuoteLineItem>(); // intermediate list
List<Opportunity> OppObjectToUpdate = new List<Opportunity>(); // intermediate list
List<Quote> QObjectToUpdate = new List<Quote>(); // intermediate list


List<OpportunityLineItem> OliList = 
            [SELECT Id, Manual_Override_Calculated_Sales_Price__c, Opportunity.RecordType.Name FROM OpportunityLineItem WHERE OpportunityId IN :OppIds]; //= :newOpp.Id];
//where Id IN :Trigger.newMap.keySet()];

List<QuoteLineItem> QliList = 
            [SELECT Id, Product_Type__c, Manual_Override_Calculated_Sales_Price__c, Quote.Opportunity.RecordTypeId, Opportunity_Line_Item_ID__c FROM QuoteLineItem WHERE QuoteId IN :ids]; //= :newOpp.Id];
//where Id IN :Trigger.newMap.keySet()];

List<Opportunity> OList = 
            [SELECT Id, Name, RecordType.Name, Description FROM Opportunity WHERE Id IN :OppIds];





//trigger on quote object for Sync   
For (Quote newQuote : newQuotes.values()) {


    Quote oldQuote  = oldQuotes.get(newQuote.Id);

    Opportunity Opp =  map_ID_Opp.get(newQuote.OpportunityID);

    OList[0].description = 'Sync working';

    if( String.ValueOf(Opp.RecordTypeId) == '01280000000Hgd8AAC') {

        Integer ListSize = QliList.size();

        for (Integer i = 0; i < ListSize; i++) {

            if(newQuote.IsSyncing == True 
               && oldQuote.IsSyncing == false) {

               For (Opportunity OppLoop : map_ID_Opp.values()) {

                   Integer ListSize2 = OliList.size();


                   for (Integer j = 0; j < ListSize2; j++) {

                       //
                       if (OliList[j].id == QliList[i].Opportunity_Line_Item_ID__c) {
                         //&& OliList[j].Manual_Override_Calculated_Sales_Price__c == true) {

                         QliList[i].Description = String.ValueOf(Opp.Id);
                         //QliList[i].Manual_Override_Calculated_Sales_Price__c = true;
                         QliList[i].Manual_Override_Calculated_Sales_Price__c = OliList[j].Manual_Override_Calculated_Sales_Price__c;

                       }    

                   }

               }




               QliObjectToUpdate.add(QliList[i]);
            }

        }

    }

update QliObjectToUpdate;

}
update OList[0];      

}
}
*/






}
1
  • To be clear it's the 'public void NewQuoteCustomFields()' method part of the class code that's causing an issue and not dropping in to the nested for loop 'for (Integer i = 0; i < QliList.size();' - hope that helps clarify, thanks
    – Jonny
    Mar 8, 2016 at 20:37

2 Answers 2

1

The problem is that your QLI list has zero items in it, and so the loop doesn't execute. That QLIList will always have zero items in an After Insert context because the Quote has not yet been fully committed to the database yet, and therefore you will always find zero QLIs looking up to it at that time.

1

Please consider these problems too:

  • Your code contains OList[0] in a couple of places meaning it always works solely with the first element in that list so as soon as there are multiple elements (e.g. some sort of bulk update of Quote) the code is likely to produce the wrong results.
  • I count four loops in total all nested inside each other. So if there are say 50 elements in each list that could be 50 * 50 * 50 * 50 = 6,250,000 trips through the innermost loop body and a guaranteed CPU governor limit exception; nested loops need to be avoided otherwise cost rises exponentially as the number of records increases and yours is an extreme case of that problem
  • Bit hard to tell with the formatting, but it looks like there are a couple of updates inside loops that again will cause governor limit problems in the bulk case

While these all relate to bulkification, no-one will thank you in the future when a simple update of multiple records causes your trigger to fail or blow up. I suggest you should do the re-design work now; you will probably also end up with less code and clearer code.

You must log in to answer this question.

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