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
John NeilanJohn Neilan 

Fire Trigger Only When Stage Moves to Closed Won

I have the trigger class below and I would like it to fire nly when an Opportunity Stage is changed to Closed Won. Can anyone helpme figure out how to do this? I have an If statement, but it fires any time an Opportunity that is Closed Won is edited, and when I tried to add another condition it gives me the error message: Error: Compile Error: Field expression not allowed for generic SObject at line 12 column 85. I only want it to fire when the Stage is initially changed. Thanks.

Trigger:
trigger MainTriggerOpportunity on Opportunity (after update) {

            ClassRenewalOppClone updater13 = new ClassRenewalOppClone();
            updater13.cloneOpp(Trigger.new);

}

Trigger Class:
public class ClassRenewalOppClone {

    public void cloneOpp(List<Opportunity> cloneOpp){

    String recordTypeName = 'Renewals';
    Map<String,Schema.RecordTypeInfo> rtMapByName = Schema.SObjectType.Opportunity.getRecordTypeInfosByName();
    Schema.RecordTypeInfo rtInfo =  rtMapByName.get(recordTypeName);
    id recType = rtInfo.getRecordTypeId();


        FOR(Opportunity opp1 : cloneOpp){
            IF(opp1.StageName.contains('Closed Won') && trigger.OldMap.get(opp1.Id).isclosed == false && opp1.RecordTypeId == recType){

            String OppId = opp1.Id;

            //Clone the Opportunity that is associated with the handoff and all createable fields 
                /* query Opportunity and then clone it */
                String soql = RecClone.getCreatableFieldsSOQL('Opportunity','Id =: OppId');
                    Opportunity opp = (Opportunity)Database.query(soql);
                    Opportunity opp2 = opp.clone(false, true);
                insert opp2;

                List<OpportunityLineItem> itemList = (List<OpportunityLineItem>)Database.query(RecClone.getCreatableFieldsSOQL('OpportunityLineItem','OpportunityId =: OppId'));

                List<OpportunityLineItem> newItemList = new List<OpportunityLineItem>();

                    for (OpportunityLineItem item : itemList) {
                        OpportunityLineItem ol = item.clone(false, true);
                            ol.totalprice = null;
                            ol.opportunityid = opp2.id;
                        newItemList.add(ol);
                    }
                insert newItemList;
            }
            }
        }
}

 
Philip NelsonPhilip Nelson
Hey John, there's probably a couple of ways to solve your problem, but here are my thoughts:
  1. As this is an After trigger, you don't have acces to Trigger.oldMap - https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_triggers_context_variables.htm
  2. So, first I would suggest making it a (after insert, after update) trigger - That way you're covered if the user creates a new record and immediately sets it to close won and saves.
  3. Then you could create a new field on Opportunity to store the previous value of the StageName and populate it with a Before trigger or possibly workflow (can't reemember the order of operations, but I think a workflow would do it and save you some coding)
  4. Then update your ClassRenewalOppClone class to compare the previous value of StageName to the current value of StageName in Trigger.new and I think you'll be set.
Hope this helps!

Once your questions is answered, please take a moment to mark a post as "Best Answer" to help others in the community with similar questions. Thanks and good luck!
John NeilanJohn Neilan
Thanks.  The link states that OldMap is onlye available in Update and Delete triggers, it doesn't mention Before or After.  I was trying to avoid doing a workflow update to trigger it, because my organization already has a number of workflows firing and I didn't want to add more to the queue, just because it's another moving part.
Philip NelsonPhilip Nelson
You can do it all with triggers, but if you want to avoid workflows, you'll need to write a second trigger (before insert, before update) that simply populate the field on Opportunity that would hold the previous Stage value.

Then the order of operation would be
  1. User saves record
  2. before insert, before update fires and saves the "old" value into the previous stage name field on Opportunity
  3. record gets inserted/updated (and a new Opportunity Id is assigned to inserted records)
  4. after insert, before update fires and does the comparison between the previous stage name and the current stage name and then does your other logic or doesn't do anything if the record isn't transitioning into the Close Won stage.

Good luck.