1
 trigger MasterOpportunityTrigger on Opportunity(before insert, after insert, 
before update, after update, 
before delete, after delete,after undelete) 
{
if (Trigger.IsAfter) 
{

  else if (Trigger.isUpdate && !triggerHelper.reEntry) 
  {
     triggerHelper.reEntry = true;

    list<Id>createIds = new list<Id>();
    for(Opportunity o:trigger.new)
    {
        //Check Opportunity is Closed('Approved')/changed frm other stage to approved
        system.debug('New stageName ->'+o.StageName +' '+'Old stageName->'+rigger.oldmap.get(o.Id).stageName);
        if(o.StageName == 'Approved' && o.stageName != trigger.oldmap.get(o.Id).stageName)
        {
            createIds.add(o.Id);
        }
    }
 // If records in createIds, send Ids along with trigger.oldmap and trigger.newmap to your class
    if(!createIds.isEmpty())
    {           
        system.debug()
        //Create a Sponsor
        CreateSponsors CSp = new CreateSponsors(createIds,trigger.oldmap,trigger.newmap);
        CSp.CSponsor();
        system.debug('*************CreateSponsors Trigger Execute');     

    }

}
}
  }
         // Class to create Sponsor record
     public class CreateSponsors 
    {     
    Map<Id, Opportunity> oldOpps;

    Map<Id, Opportunity> newOpps;

    List<Opportunity> Oplst = new List<Opportunity>();

    public CreateSponsors(List<Id> Opids,Map<Id, Opportunity> oldTriggerOpps, Map<Id, Opportunity> newTriggerOpps) 
    {
            oldOpps = oldTriggerOpps);
            newOpps = newTriggerOpps
        }

        if(newTriggerOpps.size()>0)
        {


        for(Opportunity Op : [Select id,CampaignId,Sponsored_Event__c,Sponsorship_Program__c,AccountId from Oppportunity WHERE
            ID In : Opids])
        {   
            Oplst.add(Op);
        }

    }


    public void CSponsor()
    {
        Map<ID,Schema.RecordTypeInfo> rt_Map = Opportunity.sObjectType.getDescribe().getRecordTypeInfosById();

        List<Sponsor__c> Sponlist = new List<Sponsor__c>();

            for(Opportunity Op : Oplst)
            {
                //system.debug('New Opportunity Stage->'+Op.StageName +' '+ 'New Opportunity Stage->'+oldOpps.get(Op.Id).StageName);
                //if(Op.StageName == 'Approved' && Op.StageName != oldOpps.get(Op.Id).StageName) 
                {  
                    if(rt_map.get(Op.recordTypeID).getName().containsIgnoreCase('Sponsorship') && Op.Number_of_Sponsors__c == 0) 
                    {
                        system.debug('********entry*******');
                        Sponsor__c Sp = new Sponsor__c();
                        Sp.Sponsor_Name__c = Op.AccountId;
                        Sp.Sponsored_Event__c = Op.CampaignId;
                        Sp.Sponsorship_Opportunity__c = Op.id;
                        Sp.Sponsorship_Program__c = Op.Sponsorship_Name__c;
                        Sponlist.add(Sp);
                    }
                }
            }
            if(Sponlist.size()>0)
            {
                system.debug('********entry1*******');
                insert Sponlist;
            }
        }
    }
6
  • Where's your code showing how you're passing your values into your class? Check to see if you don't have the order of trigger.newmap and trigger.oldmap reversed. Your code is written to receive trigger.oldmap first followed by trigger.newmap.
    – crmprogdev
    Commented Apr 10, 2016 at 2:31
  • @crmprogdev I am calling this class from a Main trigger, I have checked that as well //Create a Sponsor CreateSponsors CSp = new CreateSponsors(trigger.oldmap,trigger.newmap); CSp.CSponsor(); system.debug('*************CreateSponsors Trigger Execute');
    – Kiran
    Commented Apr 10, 2016 at 2:43
  • Is this on insert? If so the oldMap will display the current value
    – Eric
    Commented Apr 10, 2016 at 3:23
  • @Eric no it's on After Update, i have added the main trigger to the original post
    – Kiran
    Commented Apr 10, 2016 at 3:53
  • 1
    A change to a field other than Stage on Opportunity will cause your trigger to run and result in the same value for the old and new values of the Stage field. It is common in a trigger to ignore changes to most fields and just look for change on one specific field by making use of the oldMap.
    – Keith C
    Commented Apr 10, 2016 at 14:07

1 Answer 1

3

Since this is an AfterUpdate trigger, if you have any workflow that fires on Opportunity, one of the issues you'd be having with this trigger would likely be one that's related to recursion. After your workflow fires, your AfterUpdate trigger will fire one more time. The original values in trigger.new and trigger.old will still be the same as they were when the trigger was initiated. In your scenario, that would cause your trigger to fire a second time.

I agree with @KeithC that your trigger needs to be checking the field values before calling your class CreateSponsors. It also needs to be using a boolean variable to prevent recursion so it doesn't try to run the class a 2nd time if the trigger is called in the same execution context. See Triggers and Order of Execution for more on this.

You're wondering how to fix this. I don't know about your rollups, but would assume that they only need to be run once, so here's some suggestions on how to implement this code-wise in your afterUpdate code section:

  else if (Trigger.isUpdate && !triggerHelper.reEntry) 
  {
    // set the static boolean to true to prevent reentry
    triggerHelper.reEntry = true;

    //Roll Up amount to Grant
    GrantRollUp GrR = new GrantRollUp();
    GrR.RollUpMth(Trigger.new);
    system.debug('*************GrantRollUp Trigger Execute');

    // Roll Up amount to SponsorShip
    SponsorRollUp Sp = new SponsorRollUp(); 
    Sp.SponsorUpMth(Trigger.new);
    system.debug('*************SponsorRollUp Trigger Execute');

    list<Id>createIds = new list<Id>();
    //Create a Sponsor
    for(Opportunity o:trigger.new)
    {
        if(o.stage != trigger.oldmap.get(o.Id).stage)
        {
            createIds.add(o.Id);
        }
    }

    // If records in createIds, send Ids along with trigger.oldmap and trigger.newmap to your class
    if(!createIds.isEmpty())
    {           
        CreateSponsors CSp = new CreateSponsors(createIds,trigger.oldmap,trigger.newmap);
        CSp.CSponsor();
        system.debug('*************CreateSponsors Trigger Execute');     
    }

  }

Helper class for your public static boolean:

Public class triggerHelper{

  Public static boolean reEntry = false;

}

Change your CreateSponsors constructor to something like below so it can process the incoming Ids of records:

public CreateSponsors(List<Id>CreateIds, Map<Id, Opportunity> oldTriggerOpps, Map<Id, Opportunity> newTriggerOpps)

Further down in your class, rather than iterating on newOpps.values(), you'll need to change your code. You can either test for the Opps that contain an Id that's in CreateIds before proceeding through your loop. Or, you could just iterate on the Ids in the list and pull the field values from newOpps using newOpps.get(crId).FieldName or similar (crId is the iterator here).

Edit

The syntax I gave you originally won't properly evaluate the boolean. I normally evaluate it independently rather than using an && condition. You have two choices to correct the issue.

One option would be to change the existing line to (note: normally I do this from a dispatcher class & not from a base trigger which makes this easier):

else if (Trigger.isUpdate && triggerHelper.reEntry == false)
{
    // set the static boolean to true to prevent reentry
    boolean entry = new triggerHelper.reEntry; //need a new instance of the class
    triggerHelper.reEntry = true;

The other would be to nest the boolean == false check inside the else if as follows which is how I'd suggest doing it:

else if (Trigger.isUpdate)
{
   if(!triggerHelper.reEntry)
   { 
       // set the static boolean to true to prevent reentry
       boolean entry = new triggerHelper.reEntry; //need a new instance of the class
       triggerHelper.reEntry = true;

       .... rest of existing code

   } // end if

}// end else if

I had meant to add yesterday that it's important not to try to move this static boolean to your helper class. It needs to stay here rather than in your helper class. If located in your helper class, it would affect any other instances of the helper that might get called by another instance of your trigger, preventing them from being able to execute. That's why it needs to be located where it's at here in the trigger that calls it. It's also why you want to have the triggerHelper class to call it from.

8
  • thanks for you reply, I tried your solution but its still same issue triggerHelper.reEntry = false; list<Id>createIds = new list<Id>();for(Opportunity o:trigger.new){system.debug('trigger new stageName->'+o.stageName+' ' +'trigger old stage->'+trigger.oldmap.get(o.Id).stageName); if(o.stageName == 'Approved' && o.stageName != trigger.oldmap.get(o.Id).stageName) {createIds.add(o.Id);} [64]|DEBUG| trigger new stageName->Approved trigger old stage->Approved
    – Kiran
    Commented Apr 10, 2016 at 19:28
  • i have noticed one more thing is that if i remove the recursion check the record gets created
    – Kiran
    Commented Apr 10, 2016 at 19:29
  • I can't tell from what you posted in comments exactly how you implemented recursion check. If you were to edit your post & add your updated code, I could give you a much better response. If you don't have any workflow that fires on Opportunity, then it isn't relevant. You hadn't stated clearly what stage you wanted to create the sponsor at, so I didn't add that condition. I hope you're adding something that prevents creating duplicate records if stage moved back from Approved then forward again. I don't know if you're using approval process. If so, no workflow fires afterward.
    – crmprogdev
    Commented Apr 10, 2016 at 20:29
  • I have updated the original post with current changes that I have made, I wanted my class to create a sponsor record only when Opp stage is set to 'Approved'.There are WFRs on opportunity that update other field and but there are none that changes the stageName
    – Kiran
    Commented Apr 10, 2016 at 21:29
  • thanks for you update.I can see that static boolean is set here triggerHelper.reEntry = true; to prevent the class from running into recursion, but I am not able to get what does this line of code does boolean entry = new triggerHelper.reEntry; //need a new instance of the class (and i am not able to save the code with line)
    – Kiran
    Commented Apr 12, 2016 at 2:40

You must log in to answer this question.

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