• Qingsong Li
  • NEWBIE
  • 0 Points
  • Member since 2008

  • Chatter
    Feed
  • 0
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 1
    Questions
  • 1
    Replies
I have a trigger on opportunity level, and then a trigger on opportunity line item level, the trigger is working well for all pipeline stage with non 0% prabability when the line items less than 100 because the DML update statement limited,
The trigger in opportunity level is:
Code:
trigger OpportuntiStageUpdate on Opportunity (after update) {
    //Determine the opportuntiy been updated
   ID[] sid=New ID[trigger.new.size()]; 
   for(Opportunity s : trigger.new){
         sid.add(s.id);
   }
   OpportunityAfterTrigger.OppsUpdate(sid);
}

 
the trigger in line items is:
 
Code:
trigger OpportuntiLineItemUpdate on OpportunityLineItem (before update, before insert) {
    // Determine the ooportuntiyLineItem been updated
    // Get the opportuntiy ID which will be updated
    Set<ID> pbeIds = new Set<ID>();
    Set<ID> prodIds = new Set<ID>();
    Set<String> currCode = new Set<String>();
    for (OpportunityLineItem oli1 : Trigger.new){
        pbeIds.add(oli1.OPPORTUNITYID);
        prodIds.add(oli1.pricebookentryid);
        currCode.add(oli1.CURRENCYISOCODE);
        }
    //Now map the closedate,probability,channels
        Map<Id, Opportunity> opps= new Map<Id, Opportunity>(
            [select CLOSEDATE,
                    PROBABILITY,
                    Channel__c,
                    Manual_or_Derived__c
                    from Opportunity
                    where id In : pbeIds]);
        Map<Id, PricebookEntry> entries = new Map<Id, PricebookEntry> (
            [select product2.Description
                    from pricebookentry
                    where id In : prodIds]);
        Map<String, FX__C> fxTable = new Map<String, FX__C> ();
        for (FX__C newFX :[select Name, Exchange_Rate__C from FX__C
                    where To_Currency__C In : currCode]){
                 fxTable .put(newFX.Name,newFX);
        }

    for (OpportunityLineItem oli : Trigger.new){
        //Pending for DRI Release 1.0
        //Set BP Number to Null if Non-Derived Opps
        if (Trigger.isInsert && opps.get(oli.OPPORTUNITYID).Manual_or_Derived__c=='M'){
           oli.BP_Number__C='';
        }
        if (oli.Applicaton__C > 100 || oli.Business__C>100){
            oli.Applicaton__C.addError('Application % and Business Number % must less than or equal to 100%');
        }
        //Update Quantity for Derived Opps
        If (opps.get(oli.OPPORTUNITYID).Manual_or_Derived__c=='D' &&
            oli.Vechile_Volume__C <>0 && oli.Applicaton__C<>Null && oli.Business__C <>Null &&
            oli.Number_Per_Application__C <>0){
            oli.Quantity=math.Round(oli.Vechile_Volume__C * oli.Applicaton__C  * oli.Business__C /10000*oli.Number_Per_Application__C) ;
        }
        

        //Below code should be disable when DRI release to production
        if (Trigger.isInsert){
            oli.BP_Number__C='';
        }

        //Update Description
        oli.Description = entries.get(oli.pricebookentryid).Product2.Description;
        if (oli.BOOK_EXPECTED_CLOSED_DATE__C == null) {
            oli.BOOK_EXPECTED_CLOSED_DATE__C = opps.get(oli.OPPORTUNITYID).CLOSEDATE;
        }
                 
        //If no probability, pull the probability from header level
        if (oli.PROBABILITY__C == null) {
            oli.PROBABILITY__C = opps.get(oli.OPPORTUNITYID).PROBABILITY;
        }
        //Set All Probability to 100% when marked to win
        Set<String> won = new Set<String>();
        won.add('Win');
        won.add('Win-Carryover');
        won.add('Win-Conquest');
        won.add('Win-Extended Bookings');
        if (won.contains(oli.STAGE__C)){
            oli.PROBABILITY__C=100;
            //Check Project OI for non penetration channel
            if (oli.Projected_OI__c == null) {
                if (opps.get(oli.OPPORTUNITYID).Channel__c <> 'EEA - Penetration') {
                    oli.Projected_OI__c.addError('Project OI is required when close a opportunity!');
                }
            }
        }
        //Set All Probability to 0% when marked to lost/canceled/no quote
        Set<String> lost= new Set<String>();
        lost.add('Lost');
        lost.add('Lost-Business');
        lost.add('Lost-Opportunity');
        lost.add('Canceled');
        lost.add('No Quote');
        if (lost.contains(oli.STAGE__C)){
            oli.PROBABILITY__C=0;
        } 

        //Lost opportunity sets
        Set<String> lose= new Set<String>();
        lose.add('Lost');
        lose.add('Lost-Business');
        lose.add('Lost-Opportunity');       
        if (lose.contains(oli.STAGE__C) && oli.Other_Competitor__c == null){
            oli.Other_Competitor__c.addError('Other competitor must be fulfilled for lost opportunity');
        }
        if (lose.contains(oli.STAGE__C) && oli.Winner__c == null){
            oli.Winner__c.addError('Winner must be fulfilled for lost opportunity');
        }

        //Win opportunity sets
        Set<String> win = new Set<String>();
        win.add('Win');
        win.add('Win-Carryover');
        win.add('Win-Conquest');
        win.add('Win-Extended Bookings');        
        if (win.contains(oli.STAGE__C) && oli.Other_Competitor__c == null){
            oli.Other_Competitor__c.addError('Other competitor must be fulfilled for won opportunity');
        }
        if (win.contains(oli.STAGE__C) && oli.Projected_OI__c == null){
            oli.Projected_OI__c.addError('Project OI % must be fulfilled for won opportunity');
        }

        //Non open stage sets
        Set<String> open = new Set<String>();
        open.add('Win');
        open.add('Win-Carryover');
        open.add('Win-Conquest');
        open.add('Win-Extended Bookings');
        open.add('Lost');
        open.add('Lost-Business');
        open.add('Lost-Opportunity');
        open.add('Canceled');
        open.add('No Quote'); 
        if (oli.Decision_Factors_1__c == null && open.contains(oli.STAGE__C)) {
            oli.Decision_Factors_1__c.addError('Close a opportunity must have Decision factor');
        }
        if (oli.Incumbent_Supplier__c == null && open.contains(oli.STAGE__C)) {
            oli.Incumbent_Supplier__c.addError('Close a opportunity must have Incumbent Supplier');
        }    
        oli.CLOSE_REASON_DESCRIPTION__C = 'trigger from items';
        //Update Exchange Rate base on the Calendar Year
        if (oli.CURRENCYISOCODE =='USD'){
            oli.Exchange_Rate__C = 1.0;
        } else {
            String fromcurrCode ='USD';
            String tocurrCode = oli.CURRENCYISOCODE;
            String calendarYear = '2017';
            If (oli.CALENDAR_YEAR__C.year()<=2017) {
                calendarYear = ''+(oli.CALENDAR_YEAR__C.year());
            }
            String fxName = fromcurrCode + '-' + tocurrCode + '-' + calendarYear;
            //FX__C[] fx = [Select Exchange_Rate__C FROM FX__C Where From_Currency__C=: fromcurrCode and To_Currency__C =: tocurrCode and Year__C =: calendarYear];
            //if (fx.size()>0) {
            //    oli.Exchange_Rate__C = 1 / fx[0].Exchange_Rate__C;
            //}
            if(fxTable.containsKey(fxName)){
                oli.Exchange_Rate__C = 1 / fxTable.get(fxName).Exchange_Rate__C;
            }
        }       
    }
}

 
and then apex class is:
Code:
public class OpportunityAfterTrigger{
    public static Boolean inLineItemTrigger;
    Static testMethod void sampleOppsTestMethod() { 
        Opportunity testOpps = [SELECT Id FROM Opportunity LIMIT 1];
        ID[] sid=New ID[]{testOpps.Id}; 
       //sid.add(testOpps.id[0]);
       OpportunityAfterTrigger.OppsUpdate(sid);
    } 

    //@future (callout = true)
    public static void OppsUpdate(ID[] sid){
    //Service__c sd = new Service__c();
 
     for(Opportunity nw : [Select Id,StageName,Probability,CloseDate,Additional_Comments__C,
                            INCUMBENT_SUPPLIER__C,OTHER_COMPETITOR__C,WINNER__C,DECISION_FACTORS_1__C,PROJECTED_OI__C
                            From Opportunity
                            Where Id in :sid]){ 
                             
       //Get Opportunity ID
        Set<ID> iOppsID = New Set<ID>();
        iOppsID.add(nw.Id);
        
        //Lost stage sets
        Set<String> lost= new Set<String>();
        lost.add('Lost');
        lost.add('Lost-Business');
        lost.add('Lost-Opportunity');
        lost.add('Canceled');
        lost.add('No Quote'); 
        
        //Win stage sets
        Set<String> won = new Set<String>();
        won.add('Win');
        won.add('Win-Carryover');
        won.add('Win-Conquest');
        won.add('Win-Extended Bookings');
        
        //Anticipated stage set
        Set<String> Anticipated= new Set<String>();
        Anticipated.add('Anticipated');
        
        //Open Line Items for update
        OpportunityLineItem[] items = [select STAGE__C,PROBABILITY__C,Id,
                        OpportunityID,PRICEBOOKENTRYID,NUMBER_PER_APPLICATION__C,
                        Applicaton__c,BUSINESS__C, INCUMBENT_SUPPLIER__C,
                        OTHER_COMPETITOR__C,WINNER__C,DECISION_FACTORS_1__C,PROJECTED_OI__C   
            FROM OpportunityLineItem where OpportunityId In : iOppsID];        

        for (OpportunityLineItem a: items){
            //Set all product line items to lost/canceled/no quote if header level marked to lost
            if (lost.contains(nw.StageName)){
                a.STAGE__C = nw.StageName;
                a.PROBABILITY__C = nw.Probability;
                a.CLOSE_REASON_DESCRIPTION__C  = nw.Additional_Comments__C;
                a.BOOK_EXPECTED_CLOSED_DATE__C = nw.CloseDate;                  
                } 
                //Set open product line itmes to win if header level marked to win
                else if (won.contains(nw.StageName)){
                        if (a.STAGE__C == 'Anticipated') {
                            a.STAGE__C = nw.StageName;
                            a.PROBABILITY__C = nw.Probability;
                            a.CLOSE_REASON_DESCRIPTION__C  = nw.Additional_Comments__C; 
                            a.BOOK_EXPECTED_CLOSED_DATE__C = nw.CloseDate;                          
                            } 
                        }
                        //Set open product line items
                        else if (a.STAGE__C == 'Anticipated') {  
                                a.PROBABILITY__C = nw.Probability;
                                a.BOOK_EXPECTED_CLOSED_DATE__C = nw.CloseDate;
                        }
            if (a.INCUMBENT_SUPPLIER__C == null && nw.INCUMBENT_SUPPLIER__C != null)
                {a.INCUMBENT_SUPPLIER__C=nw.INCUMBENT_SUPPLIER__C;}
            if (a.OTHER_COMPETITOR__C == null)
                {a.OTHER_COMPETITOR__C=nw.OTHER_COMPETITOR__C;}
            if (a.WINNER__C == null)
                {a.WINNER__C=nw.WINNER__C;}
            if (a.DECISION_FACTORS_1__C == null)
                {a.DECISION_FACTORS_1__C=nw.DECISION_FACTORS_1__C;}
            if (a.PROJECTED_OI__C == null)
                {a.PROJECTED_OI__C=nw.PROJECTED_OI__C;}          
            }
        try {
        update items;
        } 
        catch (System.StringException e){
            System.debug(e);
        }
       }
    }
}

 the problems is when I change the stage to lost or cancelled (the probability 0%), the trigger will be fired twice, below is the debug information:
Code:
*** Beginning OpportuntiBPFlagUpdate on Opportunity trigger event BeforeUpdate for 006S0000002FF3p

20081103142806.509:Trigger.OpportuntiBPFlagUpdate: line 6, column 5: SelectLoop:LIST:SOBJECT:Opportunity

Cumulative resource usage:

Resource usage for namespace: (default)
Number of SOQL queries: 0 out of 20
Number of query rows: 0 out of 1000
Number of SOSL queries: 0 out of 0
Number of DML statements: 0 out of 20
Number of DML rows: 0 out of 100
Number of script statements: 46 out of 10200
Maximum heap size: 0 out of 100000
Number of callouts: 0 out of 10
Number of Email Invocations: 0 out of 10
Number of fields describes: 0 out of 10
Number of record type describes: 0 out of 10
Number of child relationships describes: 0 out of 10
Number of picklist describes: 0 out of 10
Number of future calls: 0 out of 10
Number of find similar calls: 0 out of 0
Number of System.runAs() invocations: 0 out of 0

Total email recipients queued to be sent : 0

*** Ending OpportuntiBPFlagUpdate on Opportunity trigger event BeforeUpdate for 006S0000002FF3p

*** Beginning OpportuntiStageUpdate on Opportunity trigger event AfterUpdate for 006S0000002FF3p

.........................
No profiling information for SOSL operations.

1 most expensive DML operations:
Class.OpportunityAfterTrigger.OppsUpdate: line 83, column 9: Update: LIST:SOBJECT:OpportunityLineItem: executed 2 times in 449 ms

1 most expensive method invocations:
Class.OpportunityAfterTrigger: line 11, column 24: public static void OppsUpdate(LIST:Id): executed 2 times in 525 ms


 
so this problem is caused the lost/cancelled opportunity can not have line items more than 50, my question is there any reason to cause the trigger been fire twice just for lost/canclled oppotunity? how to skip duplicated fire trigger, any suggestion on the code improvement? Thanks a lot
We have a custom object related to our Opportunity record and would like to use a mass edit of all records related to the parent Opportunity record.  The VF we have now is using the standard controller for the object as follows - however when we attempt to limit the records selected via the id passed in (i.e. https://tapp0.salesforce.com/apex/EditProducts?Opportunity__c=00660000008G7Ts ) the standard controller won't use the reference passed in to only return the records under the opportunity with the above ID - instead it shows the first 20 records of in the Products_and_Services__c object regardless of ID passed. 
 
In testing, instead of the ID of the parent Opportunity I also passed the id of just 1 specific Products_and_Services__c object (i.e. https://tapp0.salesforce.com/apex/EditProducts?id=a0060000009GxfH ) and still get the same reaction so it appears that the controller isn't referencing the id regardless of the base.  What action does the "recordSetVar" invoke and what name is it looking for ( the object API name is Products_and_Services__c, the object plural name is "Products and Services"). This record is in a master-detail relationship with the Opportunity record.
 
<apex:page standardController="Products_and_Services__c"
  recordSetVar="Products_and_Services__c" tabstyle="Opportunity" sidebar="false">
<apex:form >
<apex:pageBlock >
<apex:pageMessages />
<apex:pageBlockButtons >
  <apex:commandButton value="Save" action="{!save}"/>
  <apex:commandButton value="Cancel" action="{!cancel}"/>
</apex:pageBlockButtons>
<apex:pageBlockTable value="{!Products_and_Services__c}" var="prds">
<apex:column headerValue="Prod#"
 value="{!prds.Name}">
</apex:column>
<apex:column headerValue="Product Name"  value="{!prds.Product_Name__c}">
  </apex:column>
    <apex:column headerValue="Proc.Fee">
       <apex:inputField value="{!prds.Processing_Fee__c}"/>
     </apex:column>
     <apex:column headerValue="Mnt.Term(months)">
      <apex:inputField value="{!prds.Processing_Term__c}"/>
     </apex:column>
   </apex:pageBlockTable>
  </apex:pageBlock>
 </apex:form>
</apex:page>