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
Joe StolzJoe Stolz 

How to prevent a workflow from making my trigger fire twice

In my Org we have a picklist field for our opportunity statuses. There's a workflow that moves the stage to 'Awarded' when the status is set to 'Close-Won.' I have a trigger that creates a custom object when a certain opportunity record type reaches 'Awarded.' I only want one object created. Everything works well as long as users follow the script of moving the status instead of the status and stage. But users always seem to find a way to break what I create (or I just didn't create them well enough).

So here is my trigger:
trigger CreatePMopp on Opportunity (after update) 
{

List<Phasing_PM_Info__c> ppi = new List<Phasing_PM_Info__c>();
List<RecordType> rtypes = [Select Name, Id From RecordType where sObjectType='Opportunity' and isActive=true];
Map<String,String> opportunityRecordTypes = new Map<String,String>{};
    for(RecordType rt: rtypes) opportunityRecordTypes.put(rt.Name,rt.Id);

    for ( Opportunity o : Trigger.new )
        if ((o.RecordTypeId==opportunityRecordTypes.get('RPA Capital')) && ( o.StageName=='Close-Won') && (Trigger.oldMap.get(o.id).Status__c!='Close-Won'))
        {
            ppi.add (new Phasing_PM_Info__C(
                Name = o.Name,
                PM_Opportunity__c = o.Id));
        }
    insert ppi;
}

I could make the workflow trigger a hidden checkbox as well as update the stage, but I wanted something more straighforward. Have any of you been able to solve for this and would be willing to help me out?

Thank you,
Joe Stolz 
Best Answer chosen by Joe Stolz
SarfarajSarfaraj
Hi Joe,

Sorry I misunderstood your requirement a bit,

This is the solution you need,
Trigger,
trigger CreatePMopp on Opportunity (after update) 
{
    if(!CreatePMoppTriggerState.isExecuted)
    {
        List<Phasing_PM_Info__c> ppi = new List<Phasing_PM_Info__c>();
        List<RecordType> rtypes = [Select Name, Id From RecordType where sObjectType='Opportunity' and isActive=true];
        Map<String,String> opportunityRecordTypes = new Map<String,String>{};
        for(RecordType rt: rtypes) opportunityRecordTypes.put(rt.Name,rt.Id);

        for ( Opportunity o : Trigger.new )
            if (o.RecordTypeId==opportunityRecordTypes.get('RPA Capital') &&  (o.StageName == 'Awarded' || o.Status__c =='Close-Won'))
            {
                ppi.add (new Phasing_PM_Info__C(
                    Name = o.Name,
                    PM_Opportunity__c = o.Id));
            }
        insert ppi;
        CreatePMoppTriggerState.isExecuted = true;
    }
}
And the class,
public with sharing class CreatePMoppTriggerState
{
    public static boolean isExecuted = false;
}
--Akram

All Answers

SarfarajSarfaraj
Joe,

This is what I prefer to avoid this kind of problem.

First create a class with a static boolean variable to keep the state of the trigger,
public with sharing class CreatePMoppTriggerState
{
	public static boolean isExecuted = false;
}

Next bypass the execution of the trigger for the second time,
trigger CreatePMopp on Opportunity (after update) 
{
	if(!CreatePMoppTriggerState.isExecuted)
	{
		List<Phasing_PM_Info__c> ppi = new List<Phasing_PM_Info__c>();
		List<RecordType> rtypes = [Select Name, Id From RecordType where sObjectType='Opportunity' and isActive=true];
		Map<String,String> opportunityRecordTypes = new Map<String,String>{};
		for(RecordType rt: rtypes) opportunityRecordTypes.put(rt.Name,rt.Id);

		for ( Opportunity o : Trigger.new )
			if ((o.RecordTypeId==opportunityRecordTypes.get('RPA Capital')) && ( o.StageName=='Close-Won') && (Trigger.oldMap.get(o.id).Status__c!='Close-Won'))
			{
				ppi.add (new Phasing_PM_Info__C(
					Name = o.Name,
					PM_Opportunity__c = o.Id));
			}
		insert ppi;
		CreatePMoppTriggerState.isExecuted = true;
	}
}

--Akram
Joe StolzJoe Stolz
Thank you, Akram, but with the changes no record is created.

I created the sharing class and added your code and now nothing. Is there anything else?
SarfarajSarfaraj
Sorry I missed the if condition. Please use this instead the old condition, if ((o.RecordTypeId==opportunityRecordTypes.get('RPA Capital')) && o .Status__c=='Close-Won'))
Joe StolzJoe Stolz
Thank you for responding again, but still no record is being created. Here is the code I am using for the trigger after changing the condition:
trigger CreatePMopp on Opportunity (after update) 
{
    if(!CreatePMoppTriggerState.isExecuted)
    {
        List<Phasing_PM_Info__c> ppi = new List<Phasing_PM_Info__c>();
        List<RecordType> rtypes = [Select Name, Id From RecordType where sObjectType='Opportunity' and isActive=true];
        Map<String,String> opportunityRecordTypes = new Map<String,String>{};
        for(RecordType rt: rtypes) opportunityRecordTypes.put(rt.Name,rt.Id);

        for ( Opportunity o : Trigger.new )
            if (o.RecordTypeId==opportunityRecordTypes.get('RPA Capital') &&  o.StageName=='Close-Won')
            {
                ppi.add (new Phasing_PM_Info__C(
                    Name = o.Name,
                    PM_Opportunity__c = o.Id));
            }
        insert ppi;
        CreatePMoppTriggerState.isExecuted = true;
    }
}
And the sharing class:
public with sharing class CreatePMoppTriggerState
{
    public static boolean isExecuted = false;
}


 
Thiyagarajan Selvaraj (SFDC Developer)Thiyagarajan Selvaraj (SFDC Developer)
Can you make sure both the trigger and record type are active?
Joe StolzJoe Stolz
Yes, both are. The trigger was working (creating new records) but with the changes it no longer is.
SarfarajSarfaraj
Hi Joe,

Sorry I misunderstood your requirement a bit,

This is the solution you need,
Trigger,
trigger CreatePMopp on Opportunity (after update) 
{
    if(!CreatePMoppTriggerState.isExecuted)
    {
        List<Phasing_PM_Info__c> ppi = new List<Phasing_PM_Info__c>();
        List<RecordType> rtypes = [Select Name, Id From RecordType where sObjectType='Opportunity' and isActive=true];
        Map<String,String> opportunityRecordTypes = new Map<String,String>{};
        for(RecordType rt: rtypes) opportunityRecordTypes.put(rt.Name,rt.Id);

        for ( Opportunity o : Trigger.new )
            if (o.RecordTypeId==opportunityRecordTypes.get('RPA Capital') &&  (o.StageName == 'Awarded' || o.Status__c =='Close-Won'))
            {
                ppi.add (new Phasing_PM_Info__C(
                    Name = o.Name,
                    PM_Opportunity__c = o.Id));
            }
        insert ppi;
        CreatePMoppTriggerState.isExecuted = true;
    }
}
And the class,
public with sharing class CreatePMoppTriggerState
{
    public static boolean isExecuted = false;
}
--Akram
This was selected as the best answer
Joe StolzJoe Stolz
Thank you Akram! The change to the condition fixed it and now only one object is created. 

Thank you again for all your time and help.
SarfarajSarfaraj
You are welcome.