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
IC-TannerIC-Tanner 

APEX trigger re-firing workflow rule

Hi, I have an APEX trigger that creates a new account record and then updates the contact record associating the contact to the new account via the 'account' lookup field (parent/child). Everything works correctly except that the contact update fires two workflows rules that have already fired and based on the evaluation criteria: "When a record is created, or when a record is edited and did not previously meet the rule criteria". The APEX trigger update does not update any of the fields referenced in the workflows' criterias. I tried to replicate the problem in the org.'s sandbox, but could not. It's as though the apex trigger update is erasing the workflow history that they workflow has already run. (logically as though trigger.old is not reference or is erased for the workflow). Has anyone experienced this issue before or has any idea of why this would be occuring besides a Salesforce bug?

 

Related code below:

 

Trigger:

trigger ConverttoHouseholdTrigger on Contact (before insert, before update) {

contactHouseholdFellowship.contactHouseholdFellowshipMethod(trigger.new);

}

 

CLASS:

 

public class contactHouseholdFellowship
{
    public static void contactHouseholdFellowshipMethod(contact[] contacts)
    {   
   //Account - Build a "reversed" record type map for Account so that the record type name becomes the Key 
   //and the id is the Value.
    Map<String, Id> AccountRecTypesRev = new Map<String, Id>();
    for(RecordType rec :[select id, DeveloperName, sObjectType from RecordType 
    where sObjectType = 'Account']) {
        AccountRecTypesRev.put(rec.DeveloperName, rec.Id); }
       
    id HHaccRec = AccountRecTypesRev.get('Household');
 
    list<contact>contactList = new list<contact>();
    list<account>accountList = new list<account>();
    set<id>contactSet = new set<id>();
    map<id,id>accountMap = new map<id,id>();
    map<id,id>contactMap = new map<id,id>();
    
    for (contact c : contacts)
        {
        contactset.add(c.id);    
        }    
     
    //Contact list is necessary because we can't obtain parent 'account' field values without a SOQL query
    contactList = [select id, name, firstName, LastName, account.recordtypeId, email, Do_Not_Mail__c, 
    HasOptedOutOfEmail,DoNotCall,HomePhone,Phone,MailingStreet,MailingCity,MailingState,MailingPostalCode,
    OtherStreet,OtherCity,OtherState,OtherPostalCode,Fellowship_Status__c
    from contact where id in : contactSet];
    
    for (contact c : contactList)
        {  
        //only if contact is not already the child of a 'Household' account and only if 'offer Accepted' as
        //per current related workflow rule criteria
        if (c.account.recordTypeId != HHaccRec && c.Fellowship_Status__c == 'Offer Accepted')
            {   
            //Create map for contacts to be updated for contact.accountid update below
            contactMap.put(c.id,c.id);        
            account account = new account();
            account.name = c.firstname + ' ' + c.lastName + ' Household';
            account.Addressee__c = c.firstname + ' ' + c.lastName;
            account.Addressee_Informal__c = c.firstname;
            account.Email__c = c.Email; 
            account.Do_Not_Mail__c = c.Do_Not_Mail__c;
            account.Do_Not_Email__c = c.HasOptedOutOfEmail;
            account.Do_Not_Call__c = c.DoNotCall;
            account.recordtypeid = HHaccRec;
           If (c.HomePhone != null) 
               {
               account.phone = c.HomePhone; 
               }
               else 
               {
               account.phone = c.Phone; 
               }
             account.BillingStreet = c.MailingStreet;
             account.BillingCity = c.MailingCity;
             account.BillingState = c.MailingState;
             account.BillingPostalCode = c.MailingPostalCode;
             account.ShippingStreet = c.OtherStreet;
             account.ShippingCity = c.OtherCity;
             account.ShippingState = c.OtherState;
             account.ShippingPostalCode = c.OtherPostalCode;
             //set primary contact record for household account
             account.Primary_Contact__c = c.id;
             accountList.add(account);                                     
             }
         }  
         insert accountList;
         
         //map primary contact id to account id so we can reference which account the contact 
         //should be updated to via the parent/child account/contact relationship
         for (account a : accountList)
             {
             accountMap.put(a.Primary_Contact__c, a.id);
             }
         
     for (contact c: contacts)
         {
         //only update contacts that fit the criteria for the contactList
         if (contactMap.get(c.id) != null)
             { 
             c.accountid = accountMap.get(c.id);            
             }
         }         

     }     

 

 

MiddhaMiddha

Here is the Execution Sequence that Salesforce follows for Triggers and Workflows:

 

3. Executes all before triggers.

4. Runs most system validation steps again, such as verifying that all required fields have a non-null value, and runs any

user-defined validation rules. The only system validation that Salesforce.com does not run a second time (when the request

comes from a standard UI edit page) is the enforcement of layout-specific rules.

5. Saves the record to the database, but does not commit yet. 

6. Executes all after triggers.

7. Executes assignment rules.

8. Executes auto-response rules.

9. Executes workflow rules.

10. If there are workflow field updates, updates the record again.

11. If the record was updated with workflow field updates, fires before and after triggers one more time (and only one

more time).

Note: The before and after triggers fire one more time only if something needs to be updated. If the fields

have already been set to a value, the triggers are not fired again.