1
votes

please help me in converting my after trigger to batch apex. This trigger fires when opportunity stage changes to won. It runs through line items and checks if forecast(custom objet) exists with that acunt.if yes,iit links to them..if no,itt will create a new forecat.

my trigger works fine forr some records.but to mass update i am getting timed out error.So opting batch apex but i had never written it.pls help me.

trigger Accountforecast on Opportunity (after insert,after update) {
    List<Acc_c> AccproductList =new List<Acc_c>();
    List<Opportunitylineitem> opplinitemlist =new List<Opportunitylineitem>();
    list<opportunitylineitem > oppdate=  new list<opportunitylineitem >();
    List<Acc__c> accquery =new List<Acc__c>();
    List<date> dt =new List<date>();
    Set<Id> sProductIds = new Set<Id>();
    Set<Id> sAccountIds = new Set<Id>();
    Set<id> saccprodfcstids =new set<Id>();
    Acc__c accpro =new Acc__c();
    string aname;
    Integer i;
    Integer myIntMonth;
    Integer myIntyear;
    Integer myIntdate;

    opplinitemlist=[select Id,PricebookEntry.Product2.Name,opp_account__c,Opp_account_name__c,PricebookEntry.Product2.id, quantity,ServiceDate,Acc_Product_Fcst__c  from Opportunitylineitem                      WHERE Opportunityid IN :Trigger.newMap.keySet() AND Acc__c=''];

    for(OpportunityLineItem oli:opplinitemlist) {
    sProductIds.add(oli.PricebookEntry.Product2.id);
    sAccountIds.add(oli.opp_account__c);
    }
    accquery=[select id,Total_Qty_Ordered__c,Last_Order_Qty__c,Last_Order_Date__c,Fcst_Days_Period__c  from Acc__c where Acc__c.product__c In :sproductids and Acc__c.Account__c in :saccountids]; 

  for(Acc__c apf1 :accquery){
    saccprodfcstids.add(apf1.id);
    }        
    if(saccprodfcstids!=null){
    oppdate=[select servicedate from opportunitylineitem where Acc__c IN :saccprodfcstids ];

    i =[select count() from  Opportunitylineitem where acc_product_fcst__c in :saccprodfcstids];
    }


     for(Opportunity opp :trigger.new)
     {
         if(opp.Stagename=='Closed Won')
         {
                 for(opportunitylineitem opplist:opplinitemlist)
                 {
                     if(!accquery.isempty())
                     {
                         for(opportunitylineitem opldt :oppdate)
                         {
                             string myDate = String.valueOf(opldt);
                             myDate = myDate.substring(myDate.indexof('ServiceDate=')+12);
                             myDate = myDate.substring(0,10);                                    
                             String[] strDate = myDate.split('-');
                             myIntMonth = integer.valueOf(strDate[1]);
                             myIntYear = integer.valueOf(strDate[0]);
                             myIntDate = integer.valueOf(strDate[2]);
                             Date d = Date.newInstance(myIntYear, myIntMonth, myIntDate);
                             dt.add(d);
                         }  
                             dt.add(opp.closedate);
                             dt.sort();                   
                             integer TDays=0;
                             system.debug('*************dt:'+dt.size());
                                 for(integer c=0;c<dt.size()-1;c++)
                                 {
                                     TDays=TDays+dt[c].daysBetween(dt[c+1]);

                                 }
                                 for(Acc_product_fcst__c apf:accquery)
                                 {
                                     apf.Fcst_Days_Period__c = TDays/i;
                                     apf.Total_Qty_Ordered__c =apf.Total_Qty_Ordered__c +opplist.quantity;
                                     apf.Last_Order_Qty__c=opplist.quantity;
                                     apf.Last_Order_Date__c=opp.CloseDate ;
                                     apf.Fcst_Qty_Avg__c=apf.Total_Qty_Ordered__c/(i+1);
                                     Opplist.Acc__c =apf.Id;  
                                 }
                                                    }

                                 else{
                                 accpro.Account__c=opplist.opp_account__c;
                                 accpro.product__c=opplist.PricebookEntry.Product2.Id;
                                 accpro.opplineitemid__c=opplist.id;
                                 accpro.Total_Qty_Ordered__c =opplist.quantity;
                                 accpro.Last_Order_Qty__c=opplist.quantity;
                                 accpro.Last_Order_Date__c=opp.CloseDate;
                                 accpro.Fcst_Qty_Avg__c=opplist.quantity;
                                 accpro.Fcst_Days_Period__c=7;
                                 accproductList.add(accpro);



                                 }
                                 }

             }
         }
         if(!accproductlist.isempty()){
         insert accproductlist;
         }
         update opplinitemlist;
         update accquery;         
     }
1
This is what i had written but not sure if this works and how i can test.please guide me - satish

1 Answers

0
votes

First of all, you should take a look at this: Apex Batch Processing

Once you get a better idea on how batches work, we need to take into account the following points:

  1. Identify the object that requires more processing. Account? Opportunity?
  2. Should the data be maintained across batch calls? Stateful?
  3. Use correct data structure in terms of performance. Map, List?

  4. From your code, we can see you have three objects: OpportunityLineItems, Accounts, and Opportunities. It seems that your account object is using the most processing here.

  5. It seems you're just keeping track of dates and not doing any aggregations. Thus, you don't need to maintain state across batch calls.

  6. Your code has a potential of hitting governor limits, especially memory limits on the heap. You have a four-nested loop. Our suggestion would be to maintain opportunity line items related to Opportunities in a Map rather than in a List. Plus, we can get rid of those unnecessary for loops by refactoring the code as follows:

Note: This is just a template for the batch you will need to construct.

globalglobal Database.QueryLocator start(Database.BatchableContext BC) class AccountforecastBatch implements Database.Batchable<sObject>
{
    global Database.QueryLocator start(Database.BatchableContext BC)
    {
       // 1. Do some initialization here: (i.e. for(OpportunityLineItem oli:opplinitemlist) {sProductIds.add(oli.PricebookEntry.Product2.id)..}
       // 2. return Opportunity object here:  return Database.getQueryLocator([select id,Total_Qty_Ordered__c,Last_Order_Qty ....]);
    }

    global void execute(Database.BatchableContext BC, List<sObject> scope)
    {
       // 1. Traverse your scope which at this point will be a list of Accounts
       // 2. You're adding dates inside the process for Opportunity Line Items. See if you can isolate this process outside the for loops with a Map data structure.
       // 3. You have 3 potential database transactions here (insert accproductlist;update opplinitemlist; update accquery; ). Ideally, you will only need one DB transaction per batch.If you can complete step 2 above, you might only need to update your opportunity line items. Otherwise, you're trying to do more than one thing in a method and you will need to redesign your solution           
    }

    global void finish(Database.BatchableContext BC) 
    {
       // send email or do some other tasks here
    }
}