1

I am using an MVC to calculate all discounts to valid products and display them out on a Visualforce page.

The problem I encounter is if the Quote has over 500 quote line items, the MVC will finish calculating discounts for each QuoteLineItem but the Visualforce page will end up timing out since it takes about 1-2 minutes to finish processing.

In my Model I currently perform queries from Quote, Account, Opportunity, QuoteLineItem, PricebookEntry, and Product2 in order to get the necessary information to create a discount list. I validated that the query limit was not reached and still had plenty of allowance.

After reviewing the DEBUG log, I noticed that populating the productWrapper was taking a lot of time, due to the for loops shown below.

Does anyone have a better suggestion for this approach?

    for(Integer i=0;i<quoteLineItemList.size();i++)
        PricebookEntryIdx.add(quoteLineItemList.get(i).PricebookEntryId);
    List<PricebookEntry> priceBookEntryList = [SELECT ID,NAME,PRODUCT2ID,UNITPRICE FROM PricebookEntry Where ID in: PricebookEntryIDx]; 
    for(Integer j=0;j<priceBookEntryList.size();j++)
        Product2IDx.add(priceBookEntryList.get(j).Product2Id);
    List<Product2> product2List = [SELECT ID,NAME,DISCOUNTABLE__C,PRODUCTCODE FROM PRODUCT2 WHERE ID IN:Product2Idx];

    /** Wrapper class used to take fields from three different Objects and create a pseudo Object **/   
    for(QuoteLineItem QLI: quoteLineItemList){
        productWrapper fullProductList = new productWrapper();
        fullProductList.QLTID = QLI.ID;
        fullProductList.Discount = QLI.Discount;
        fullProductList.Product_Code = QLI.Product_Code__c;
        fullProductList.Quantity = QLI.Quantity;
        fullProductList.PricebookEntryId = QLI.PricebookEntryId;
        for(PricebookEntry PBE: priceBookEntryList){
            if(PBE.ID == QLI.PricebookEntryId){
                fullProductList.Product2Id = PBE.Product2Id;
                fullProductList.UnitPrice = PBE.UnitPrice;
                break;
            }   
        }
        for(Product2 P2:product2List){
            if(P2.ProductCode == QLI.Product_Code__c){
                fullProductList.ProductName = P2.Name;
                fullProductList.Discountable = P2.Discountable__c;
                break;
            }
        }
        /** Separating the Wrapper list into 4 different Lists: Non Discountable, Service, Support and the rest are all Products **/    
        if(fullProductList.Discountable == 'No'){ //Non Discountable List
            nonDiscountableList.add(fullProductList);
            nonDiscountedUnitPriceTally+=fullProductList.UnitPrice*fullProductList.Quantity;
        }       
        else
            if(fullProductList.Product_Code.startsWith('SV')){ // Service List
                serviceDiscountList.add(fullProductList);
                servicesUnitPriceTally+=fullProductList.UnitPrice*fullProductList.Quantity;   
            }   
        else
            if(fullProductList.Product_Code.startsWith('M')){ // Support List
                supportDiscountList.add(fullProductList);                   
                supportUnitPriceTally+=fullProductList.UnitPrice*fullProductList.Quantity;
            }   
        else{ 
            productDiscountList.add(fullProductList);   //Product List
            productUnitPriceTally+=fullProductList.UnitPrice*fullProductList.Quantity;
        }
    }
2

Loops nested like this:

    for(QuoteLineItem QLI: quoteLineItemList){
        for(PricebookEntry PBE: priceBookEntryList){
            if(PBE.ID == QLI.PricebookEntryId){
                ....
            }
        }
    }

really hurt performance as the number of objects grow because instead of the time taken being linear with the number of objects it grows as the square of the number of objects.

The best fix is to query both the parent and child objects at once (if the data model allows) by changing to relationship queries. It is then no longer necessary to search for the matching child objects as they are just available via the relationship _r fields.

If that can't be done then you need to create your own maps for the child objects so that instead of iterating over a list of objects you can jump directly to the right object via the map key.

Here is a (not tested) illustration of the relationship query approach:

QuoteLineItem[] quoteLineItemList = [
        select PricebookEntry.Product2Id2, PricebookEntry.UnitPrice,
                PricebookEntry.Product2.Name, PricebookEntry.Product2.Discountable__c,
                ...
        from QuoteLineItem
        where ...
        ];

for(QuoteLineItem QLI: quoteLineItemList){
    ...
    // No loops needed here
    if (QLI.PricebookEntry != null) {
        fullProductList.Product2Id = QLI.PricebookEntry.Product2Id;
        fullProductList.UnitPrice = QLI.PricebookEntry.UnitPrice;
        if (QLI.PricebookEntry.Product2 != null) {
            fullProductList.ProductName = QLI.PricebookEntry.Product2.Name;
            fullProductList.Discountable = QLI.PricebookEntry.Product2.Discountable__c;
        }
    }
    ...

}
| improve this answer | |
  • thanks for the quick reply, I will try the approach of fixing the query in order to remove the nested for loop. – enph Jul 30 '14 at 14:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.