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
singledotsingledot 

Making sure a trigger only fires once...again please?

I've read a couple of similar questions on this board regarding triggers.

Right now, learning Apex, I'm trying to have an event fire from a custom object that has certain "tasks" assigned to a certain user. When a task field is assigned to a user through a lookup field, the trigger ideally would fire an event to that user. What I'm finding is that the trigger is firing everytime the record is saved. I DO need the trigger to fire if the field is just be assigned (so as an update) but once the field is assigned and the event created, I don't need multiple events.

 

This is what I did. Which is not working
.

Created this class:

 

public class Recursionblock {
   public static boolean alreadyRan = false;
}

 

 

Created this trigger (with a lot of help from this community):

 

trigger createPreCheckoutEvent on Set_Up_Transition__c (after insert, after update) {

    if (!Recursionblock.alreadyRan) {
    
    Event[] eventList = new Event[0];
    Map<Id,Property__c> addresses = new Map<Id,Property__c>();

// Aggregate
    for(Set_Up_Transition__c suObj:Trigger.new) {
            addresses.put(suObj.Property_Address__c,null);
    }
    
// Query
    addresses.remove(null);
    addresses.putAll([SELECT Id,Name FROM Property__c WHERE Id IN :addresses.keySet()]);

// Update
    for(Set_Up_Transition__c suOBj:Trigger.new) {
        if(suObj.Pre_Check_Out_Communication_2__c != null) {
            eventList.add(
                new Event(
                   OwnerId = suObj.Pre_Check_Out_Communication_2__c,
                   Subject = 'Pre Check Out Communication',
                   DurationInMinutes = 60,
                   StartDateTime = suObj.Pre_Check_Out_Communication_Time__c,
                   Location = addresses.containsKey(suObj.Property_Address__c)?addresses.get(suObj.Property_Address__c).Name:''));
        }
    }
    insert eventList;
    Recursionblock.alreadyRan = true;
    }
}

 

 

Needless to say, the trigger is still firing every time I save the record.

Best Answer chosen by Admin (Salesforce Developers) 
singledotsingledot

I really wanted to not use a checkbox on my object but I did get part of this to work. In the interest of the thread, I created a checkbox (Event_Pre_Check_Out_Created__c) on my object and defaulted to false.

 

Here is the code:

trigger createPreCheckoutEvent on Set_Up_Transition__c (before insert, before update) {

    Event[] eventList = new Event[0];
    Map<Id,Property__c> addresses = new Map<Id,Property__c>();

// Aggregate
    for(Set_Up_Transition__c suObj:Trigger.new) {
            addresses.put(suObj.Property_Address__c,null);
    }
// Query
    addresses.remove(null);
    addresses.putAll([SELECT Id,Name FROM Property__c WHERE Id IN :addresses.keySet()]);

// Update
    for(Set_Up_Transition__c suOBj:Trigger.new) {
        if(suObj.Pre_Check_Out_Communication_2__c != null && suObj.Event_Pre_Check_Out_Created__c != null && suObj.Event_Pre_Check_Out_Created__c == false) {
            eventList.add(
                new Event(
                   OwnerId = suObj.Pre_Check_Out_Communication_2__c,
                   Subject = 'Pre Check Out Communication',
                   DurationInMinutes = 60,
                   StartDateTime = suObj.Pre_Check_Out_Communication_Time__c,
                   Location = addresses.containsKey(suObj.Property_Address__c)?addresses.get(suObj.Property_Address__c).Name:''));
      
    insert eventList;
    suObj.Event_Pre_Check_Out_Created__c = true;
     }
    }
}

 This works great for getting the trigger to only fire once, but will not work for more complex business needs, like if the task was reassigned to a different person...where ideally the event would then fire for that person and delete the event for the other.

All Answers

singledotsingledot

I'm still completely confused as to why this isn't working. Found another thread and tried that example.

This doesn't work either

 

Class:

 

public class TriggerHelperClass {   

    private static boolean flagvalue = false;


    public static boolean hasAlreadyfired() {
        return flagvalue;
    }
    
    public static void setAlreadyfired() {
        flagvalue = true;
    }


}

 Trigger:

 

trigger createPreCheckoutEvent on Set_Up_Transition__c (after insert, after update) {

    Event[] eventList = new Event[0];
    Map<Id,Property__c> addresses = new Map<Id,Property__c>();

// Aggregate
    for(Set_Up_Transition__c suObj:Trigger.new) {
            addresses.put(suObj.Property_Address__c,null);
    }
// Query
    addresses.remove(null);
    addresses.putAll([SELECT Id,Name FROM Property__c WHERE Id IN :addresses.keySet()]);

// Update
    for(Set_Up_Transition__c suOBj:Trigger.new) {
        if(suObj.Pre_Check_Out_Communication_2__c != null && !TriggerHelperClass.hasAlreadyfired()) {
            eventList.add(
                new Event(
                   OwnerId = suObj.Pre_Check_Out_Communication_2__c,
                   Subject = 'Pre Check Out Communication',
                   DurationInMinutes = 60,
                   StartDateTime = suObj.Pre_Check_Out_Communication_Time__c,
                   Location = addresses.containsKey(suObj.Property_Address__c)?addresses.get(suObj.Property_Address__c).Name:''));
      
    insert eventList;
    TriggerHelperClass.setAlreadyfired();
      }
    }
}

 Basically, as before stated, I need the trigger to fire once the task is assigned to a user only once (is now assigning a duplicate task everytime the record is resaved) OR if the user is changed

singledotsingledot

I really wanted to not use a checkbox on my object but I did get part of this to work. In the interest of the thread, I created a checkbox (Event_Pre_Check_Out_Created__c) on my object and defaulted to false.

 

Here is the code:

trigger createPreCheckoutEvent on Set_Up_Transition__c (before insert, before update) {

    Event[] eventList = new Event[0];
    Map<Id,Property__c> addresses = new Map<Id,Property__c>();

// Aggregate
    for(Set_Up_Transition__c suObj:Trigger.new) {
            addresses.put(suObj.Property_Address__c,null);
    }
// Query
    addresses.remove(null);
    addresses.putAll([SELECT Id,Name FROM Property__c WHERE Id IN :addresses.keySet()]);

// Update
    for(Set_Up_Transition__c suOBj:Trigger.new) {
        if(suObj.Pre_Check_Out_Communication_2__c != null && suObj.Event_Pre_Check_Out_Created__c != null && suObj.Event_Pre_Check_Out_Created__c == false) {
            eventList.add(
                new Event(
                   OwnerId = suObj.Pre_Check_Out_Communication_2__c,
                   Subject = 'Pre Check Out Communication',
                   DurationInMinutes = 60,
                   StartDateTime = suObj.Pre_Check_Out_Communication_Time__c,
                   Location = addresses.containsKey(suObj.Property_Address__c)?addresses.get(suObj.Property_Address__c).Name:''));
      
    insert eventList;
    suObj.Event_Pre_Check_Out_Created__c = true;
     }
    }
}

 This works great for getting the trigger to only fire once, but will not work for more complex business needs, like if the task was reassigned to a different person...where ideally the event would then fire for that person and delete the event for the other.

This was selected as the best answer
singledotsingledot

And I'd still love to know why the class I initially constructed didn't work.

MoggyMoggy

not sure but i think your issue is the double negative value
First of all I would switch the setting hasAlready...True and setAlready to false-
AS --&& !TriggerHelperClass.hasAlreadyfired()

if that var is false and you check with a leading ! it becomes true
so the update will happen, , I know the human logic tells otherwise but in codeing you should always try to think like the machine

only other option to test is to mask the true condition
if(suObj.Pre_Check_Out_Communication_2__c != null && ( !(TriggerHelperClass.hasAlreadyfired())))
the ! within bracers and the Trigger.... within bracers