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
Merry SMerry S 

Updating Contact from unrelated object using State/Area Code(custom field) to match

I am trying to update the Contact with State & time zone on insert and update. I have an object that holds a list of all areacodes and each record includes time zone and state. What I want to happen is that when a contact is inserted the trigger will match the area code (custom formula field on the contact) to my custom object and then populate the state and timezone. (If the state is null - which is not part of the trigger I will be showing). Also, on update I want the trigger to look check if the state is being updated, and if so, update the time zone to match the new state. (Sometimes contacts have cell numbers which do not correlate to the state in which they reside.) I have tried many iterations of this tirgger and below is what I have ended up with that "works" to varying degrees.
I have tested each section (the insert and the update) and they "work" - but not how I would like.

This part of the trigger works as long as I do not have the If(Trigger.IsInsert), once I add that it no longer fires.
trigger Contact_PopulateAreaCode on Contact (before update, before insert) {
    // first iteration to get the area codes    
    Set<String> contactAC = new Set<String>();
    Set<String> contactST = new Set<String>();


    If(Trigger.isInsert){
     
     for(Contact newContact:Trigger.new){
     if(newContact.LeadSource != 'New Customer Contact')
       contactAC.add(newContact.Area_code__c);
          System.debug('*****************************************contactAC has ' + contactAC.size());
     }
    
     // populate the map
     List <AreaCodeTimeZoneMap__c> IMaps = [SELECT Id, Area_Code__c, State_Province__c, Std_Time_Zone__c FROM AreaCodeTimeZoneMap__c WHERE Area_Code__c IN :contactAC];
    System.debug('*****************************************Maps has ' + imaps.size());
     Map<String, AreaCodeTimeZoneMap__c> ACTZMap = new  Map<String, AreaCodeTimeZoneMap__c>();
    for(AreaCodeTimeZoneMap__c a : IMaps){
        ACTZMap.put(a.Area_code__c,a);
    }

	     // final iteration to set state & timezone based on Area Code
 
     for(Contact newContact:Trigger.new){
         if(newContact.LeadSource != 'Customer Website User' && (newContact.MailingCountry=='United States'||newContact.MailingCountry=='Canada' )){
             AreaCodeTimeZoneMap__c mp = ACTZMap.get(newContact.Area_Code__c);
             if(mp != null){
             newContact.MailingState = mp.State_Province__c;
             newContact.Time_Zone__c = mp.Std_Time_Zone__c;
             }
          }
     } 

}
In the second part of the trigger I have a few issues. 1 - the code used to find the new value of the state and the old value return only the old value. I assume that has something to do with it being a before trigger, but I have read that it is possible to get the trigger.oldMap in a before trigger. I have it all commented out (One note, and I swear this worked before - the part that has Contact oldContact = Trigger.old; come back with an error of "Illegal assignment from List<Contact> to Contact".
With all of that commented out the trigger will fire, but it updates the time zone to the based on the old state. I was thinking the commented code would fix this, but even when it did work the old and new values were always the same - and always the old value.
if(Trigger.isUpdate){
     for(Contact newContact:Trigger.new){
        //Contact oldContact = Trigger.old;
    		//String oldState = oldContact.MailingState;
   			//String newState = newContact.MailingState;	
        // System.debug('*****************************************Old State ' + oldState);
        // System.debug('*****************************************New State ' + newState); 
        // if(newState != oldState){
     if(newContact.LeadSource != 'New Customer Contact')
       contactST.add(newContact.MailingState);
          System.debug('*****************************************contactST has ' + contactST.size());
     }

     // populate the map
     List <AreaCodeTimeZoneMap__c> UMaps = [SELECT Id, Area_Code__c, State_Province__c, Std_Time_Zone__c FROM AreaCodeTimeZoneMap__c WHERE State_Province__c IN :contactST];
    System.debug('*****************************************Maps has ' + umaps.size());
     Map<String, AreaCodeTimeZoneMap__c> ACTZMap1 = new  Map<String, AreaCodeTimeZoneMap__c>();
    for(AreaCodeTimeZoneMap__c a : UMaps){
        ACTZMap1.put(a.State_Province__c,a);
    }
	     // final iteration to set timezone based on State
 
     for(Contact newContact:Trigger.new){
              AreaCodeTimeZoneMap__c mp = ACTZMap1.get(newContact.MailingState);
             newContact.Time_Zone__c = mp.Std_Time_Zone__c;
     }
        
}
}

I also know that my updates are happening in the for loop. I have tried a couple of things to get them out of the loop (assigning a list) but I cannot seem to figure that out. I am not asking you to rewrite the code for me to include everything I need - I just need some direction as I have spent hours combing through discussion forums and trying new things - but I cannot seem to get my head around it enough to make it happen.

Thanks in advance.
 
Best Answer chosen by Merry S
Ravi Dutt SharmaRavi Dutt Sharma
An update to this :
If you compare MailingStateCode instead of MailingState, it will give the correct values in Trigger.oldMap and Trigger.newMap. If we have State country picklist enabled in the org, then I think we should compare MailingStateCode instead of MailingState.

All Answers

Ravi Dutt SharmaRavi Dutt Sharma
Hi Merry,

Try below code for update scenario. Let me know if it works.
BTW, I like your statment "One note, and I swear this worked before" :D
if(Trigger.isUpdate){
     for(Contact newContact:Trigger.new){
        
        if(newContact.MailingState != Trigger.oldMap.get(newContact).MailingState){ // check whether the MailingState has changed
		 if(newContact.LeadSource != 'New Customer Contact')
		   contactST.add(newContact.MailingState);
			  System.debug('*****************************************contactST has ' + contactST.size());
		 }

     // populate the map
     List <AreaCodeTimeZoneMap__c> UMaps = [SELECT Id, Area_Code__c, State_Province__c, Std_Time_Zone__c FROM AreaCodeTimeZoneMap__c WHERE State_Province__c IN :contactST];
    System.debug('*****************************************Maps has ' + umaps.size());
     Map<String, AreaCodeTimeZoneMap__c> ACTZMap1 = new  Map<String, AreaCodeTimeZoneMap__c>();
    for(AreaCodeTimeZoneMap__c a : UMaps){
        ACTZMap1.put(a.State_Province__c,a);
    }
	     // final iteration to set timezone based on State
 
     for(Contact newContact:Trigger.new){
              AreaCodeTimeZoneMap__c mp = ACTZMap1.get(newContact.MailingState);
             newContact.Time_Zone__c = mp.Std_Time_Zone__c;
     }
        
}
}

 
Merry SMerry S
Ravi,

Haha - well, I really did feel like it had worked, but obviously it had not. :)

I tried using the code you provided but it was still the same issue of old and new returning the same value. What I ended up doing was creating a field on the account to hold the prior value of the mailingstate field and had it update from a WF. I was then able to compare old and new successfully. As for the problem with the if(trigger.isInsert) I found what I hope is a workaround.

If you, or anyone else can see a way to make this better, I am open to suggestions. But for now, it works (pre UAT)..
 
trigger Contact_PopulateAreaCode on Contact (before update, before insert) {
    // first iteration to get the area codes    
    Set<String> contactAC = new Set<String>();
    Set<String> contactST = new Set<String>();

     
     for(Contact newContact:Trigger.new){
     if((newContact.LeadSource != 'New Customer Contact' && newContact.State_Old_Value__c == 'new') ||
        (newContact.LeadSource != 'New Customer Contact' && (newContact.State_Old_Value__c != null && newContact.MailingState == null )))
       contactAC.add(newContact.Area_code__c);
          System.debug('*****************************************contactAC has ' + contactAC.size());
     }
    
     // populate the map
     List <AreaCodeTimeZoneMap__c> IMaps = [SELECT Id, Area_Code__c, State_Province__c, Std_Time_Zone__c FROM AreaCodeTimeZoneMap__c WHERE Area_Code__c IN :contactAC];
    System.debug('*****************************************Maps has ' + imaps.size());
     Map<String, AreaCodeTimeZoneMap__c> ACTZMap = new  Map<String, AreaCodeTimeZoneMap__c>();
    for(AreaCodeTimeZoneMap__c a : IMaps){
        ACTZMap.put(a.Area_code__c,a);
    }

	     // final iteration to set state & timezone based on Area Code
 
     for(Contact newContact:Trigger.new){
         if(newContact.LeadSource != 'Customer Website User'){
             AreaCodeTimeZoneMap__c mp = ACTZMap.get(newContact.Area_Code__c);
             if(mp != null){
             newContact.MailingState = mp.State_Province__c;
             newContact.Time_Zone__c = mp.Std_Time_Zone__c;
             }
          }
     

}
if(Trigger.isUpdate){

     for(Contact newContact:Trigger.new){
         if(newContact.MailingState != newContact.State_old_value__c){
		 if(newContact.LeadSource != 'New Customer Contact')
		   contactST.add(newContact.MailingState);
			  System.debug('*****************************************contactST has ' + contactST.size());
            System.debug('*****************************************Old ' + newContact.State_old_value__c);
            System.debug('*****************************************New ' + newContact.MailingState);
		 }
     }

     // populate the map
     List <AreaCodeTimeZoneMap__c> UMaps = [SELECT Id, Area_Code__c, State_Province__c, Std_Time_Zone__c FROM AreaCodeTimeZoneMap__c WHERE State_Province__c IN :contactST];
    System.debug('*****************************************UMaps has ' + umaps.size());
     Map<String, AreaCodeTimeZoneMap__c> ACTZMap1 = new  Map<String, AreaCodeTimeZoneMap__c>();
    for(AreaCodeTimeZoneMap__c a : UMaps){
        ACTZMap1.put(a.State_Province__c,a);
    }
	     // final iteration to set timezone based on State

     for(Contact newContact:Trigger.new){
              AreaCodeTimeZoneMap__c mp = ACTZMap1.get(newContact.MailingState);
                      if(mp != null){
             newContact.Time_Zone__c = mp.Std_Time_Zone__c;
         }
     }
       
}
    }


 
Ravi Dutt SharmaRavi Dutt Sharma
Hi Merry,

This seems to be a bug in Salesforce. I guess the trigger is not able to handle compound fields properly. I have raised an idea on IdeaExchange forum for this. Please upvote this so that it gets fixed in future releases. Thanks.

https://success.salesforce.com/ideaView?id=0873A0000003OoGQAU
Ravi Dutt SharmaRavi Dutt Sharma
An update to this :
If you compare MailingStateCode instead of MailingState, it will give the correct values in Trigger.oldMap and Trigger.newMap. If we have State country picklist enabled in the org, then I think we should compare MailingStateCode instead of MailingState.
This was selected as the best answer
Merry SMerry S
Thanks Ravi, I cannot beleive I never thought to try that. And I voted on the idea.
Ravi Dutt SharmaRavi Dutt Sharma
Merry, thanks for voting the idea. Please mark the question as solved if you got the solution that you were looking for.