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
ABC XYZ 39ABC XYZ 39 

After insert and After update triggers firing at the same time for a new record creation - Reposting

Basic Logic is :
For insert trigger case --- processNewContact is the class. 
Whenever a new contact is created of the type 'customer', it is inserted into objQ object.
Now, when the contact is created, a custom field 'PID' needs to be updated from external system into Salesforce. 


After Update Trigger Case --->
Whenever an existing contact is updated - If any of the fields change with 'PID' value null, updated record needs to be pushed into objQ object. Then the external system will reply back with 'PID' value which will assigned to Salesforce record.

Behavior now :
Whenever a  new contact  is created, 2 copies of the same record are inserted into the Queue object. This I am assuming is happening because 'after insert' and 'after update' triggers aer firing for every new contact created. 


I don't understand why this is happening. There are no workflows impacting the behavior . It has to do with logic in the class below. UpdateContact  is similar to processNewContact method below ; only difference is checking whether any of the fields have changed. If they have and 'PID' value is null, record will be pushed into Queue object.  Please assist. I have been trying to fix for sometime without any luck...I need to fix it as soon as possible. I had posted the same earlier but didn't get any reply.  I am new to  programming and can't figure out how to fix it. Thanks...




public static void processNewContact(Map<Id, Contact> newContactMap, Map<Id, Contact> oldContactMap)
02{
03    List<Queue__c> lstQ = new List<Queue__c>();   
04    Id idContactCustomerRecType;
05    Map<Id, String> mapIdToAccountName = new Map<Id, String>();
06     
07    // Assuming Contact type Name is 'Customer'
08    List<RecordType> lstRecType = new List<RecordType>([SELECT Id FROM RecordType WHERE Name = 'Customer' AND SobjectType = 'Contact']);
09    if(!lstRecType.isEmpty())
10        idContactCustomerRecType = lstRecType[0].Id;
11         
12    if(idContactCustomerRecType != null)
13    {
14        // we can;t fetch parent fields directly, so collect account Ids first
15        for(Contact objContact : newContactMap.values())
16        {
17            if(objContact.AccountId != null)
18                mapIdToAccountName.put(objContact.AccountId, null);
19        }
20    }
21     
22    // iterate over account with matching collected Ids and fetch names
23    for(Account objAccount : [SELECT Id, Name FROM Account WHERE Id IN : mapIdToAccountName.keySet()])
24    {
25        mapIdToAccountName.get(objAccount.Id, objAccount.Name);
26    }
27     
28    // loop again to perform business logic
29    for(Contact objContact : newContactMap.values())
30    {
31        /* 1. If Contact RecordType is Customer AND if LastName != Account.Name, insert Queue **/
32        if(objContact.RecordTypeId == idContactCustomerRecType && objContact.AccountId != null && objContact.LastName != mapIdToAccountName.get(objContact.AccountId))
33        {
34            Queue__c objQ = new Queue__c();
35            objQ.Object_Name__c='Contact';
36            objQ.Description__c= 'New Contact';
37            objQ.Record_Id__c = objContact.Id;
38            objQ.Notification_Timestamp__c= objContact.CreatedDate;
39            lstQ.add(objQ);
40        }
41             
42    }
43     
               isCreated = true;

44    if(!lstQ.isEmpty())
45        insert lstQ;
46     
47  }

Trigger Invocation :  After UPDATE
if(ContactController.isCreated==false ){
        ContactController.UpdateContact(Trigger.newMap, Trigger.oldMap);
    } 

AFTER INSERT 
    ContactController.processNewContact(Trigger.newMap, Trigger.oldMap);

 
Nayana KNayana K
Please post whole code.

For Trigger Invocation you can do like this :
// after update
if(Trigger.isUpdate && Trigger.isAfter){
        ContactController.UpdateContact(Trigger.newMap, Trigger.oldMap);
    } 
// after insert
else if(Trigger.isInsert && Trigger.isAfter){
    ContactController.processNewContact(Trigger.newMap, Trigger.oldMap);
}
 
Ajay GautamAjay Gautam
Looks like you have a Static variable isCreated in ContactController, and running Update and/or Insert scenario based on that. Let's avoid that and have these method wrapped inside if statements per Trigger.isUpdate for Update, and Trigger.isInsert for Insert scenario instead.

Please retry and let us know the results. Good Luck!
ABC XYZ 39ABC XYZ 39
Hi Nayana, Thanks for posting. I tried to invoke using the code  you posted. Records are still getting inserted twice.


Below is the code for updatecontact.  If few of the fields are getting modified, then we push into the queue. (OR)

If during the update, if P id is found to be still missing, records will be pushed into the queue object if there are any updates to the record.



 public static void UpdateContact(Map<Id, Contact> newContactMap, Map<Id, Contact> oldContactMap) {

      List<Queue__c> lstQ = new List<Queue__c>();   
       Id idContactCustomerRecType;
    Map<Id, String> mapIdToAccountName = new Map<Id, String>();

    List<RecordType> lstRecType = new List<RecordType>([SELECT Id FROM RecordType WHERE Name = 'Customer' AND SobjectType = 'Contact']);
    //Considering only 'Customer' type of contact records

    if(!lstRecType.isEmpty())

        idContactCustomerRecType = lstRecType[0].Id;

    if(idContactCustomerRecType != null)

    {

        // Can't fetch parent fields directly, so collecting account Ids first

        for(Contact objContact : newContactMap.values())

        {

            if(objContact.AccountId != null)

                mapIdToAccountName.put(objContact.AccountId, null);

        }

    }

     

    // iterate over account with matching collected Ids and fetch names
   
    for(Account objAccount : [SELECT Id, Name FROM Account WHERE Id IN : mapIdToAccountName.keySet()])

    {

        mapIdToAccountName.put(objAccount.Id, objAccount.Name);

    }  


        for(Contact objContact: newContactMap.values()) { 
        
              //Checking only for changes in field values
              
               if(objContact.RecordTypeId == idContactCustomerRecType && objContact.AccountId != null && objContact.LastName != mapIdToAccountName.get(objContact.AccountId)) {
           
                                         if( (objContact.A__c!= oldContactMap.get(objContact.Id).A__c) || (objContact.B__c!= oldContactMap.get(objContact.Id).B__c) ||
                     (objContact.D__c!= oldContactMap.get(objContact.Id).D__c) 
                      
                      {
     
                           
                           //Adding all contact records with changed values to Queue object
                             
                           Queue__c objQ = new Queue__c()
                           objQ.Object_Name__c='Contact';
                           objQ.Description__c=' Updated Contact';
                           objQ.Record_Id__c = objContact.Id;
                           objQ.Notification_Timestamp__c= objContact.LastModifiedDate;
                           lstQ.add(objQ);       
                   
         
                        }
         
      
        
        //Considering all contact records with null values for MDM Person Ids
         
 
             
    else if (objContact.PID__c==null)

       {
    
                           Queue__c objQ = new Queue__c()

                           objQ.Object_Name__c='Contact';
                           objQ.Description__c= 'PID Missing';
                           objQ.Record_Id__c = objContact.Id;
                           objQ.Notification_Timestamp__c= objContact.LastModifiedDate;
                           lstQ.add(objQ);       
      
    }
    
    
                  }
                              
            }
            
           isCreated = true;
       if(!lstQ.isEmpty())  insert lstQ;

            }
            
            
ABC XYZ 39ABC XYZ 39
Hi Ajay, thanks for the reply. I changed the invocation as per Nayana's post. It's still not working. Is it as similar invocation that you meant I should do...I have removed the flags. 
Nayana KNayana K
if(Trigger.isInsert && Trigger.isAfter)
{
	ContactController.processContact(Trigger.newMap, Trigger.oldMap);
}
else if(Trigger.isUpdate && Trigger.isAfter)
{
	ContactController.processContact(Trigger.newMap, Trigger.oldMap);
}



// call process contact on both insert and update
public  void processContact(Map<Id, Contact> newContactMap, Map<Id, Contact> oldContactMap) 
{ 
    List<Queue__c> lstQ = new List<Queue__c>();    
	Id idContactCustomerRecType;
	Map<Id, String> mapIdToAccountName = new Map<Id, String>();
	
	// Assuming Developer Name is 'Customer'
	List<RecordType> lstRecType = new List<RecordType>([SELECT Id FROM RecordType WHERE DeveloperName = 'Customer' AND SobjectType = 'Contact']);
	if(!lstRecType.isEmpty())
		idContactCustomerRecType = lstRecType[0].Id;
		
	if(idContactCustomerRecType != null)
	{
		// we can;t fetch parent fields directly, so collect account Ids first
		for(Contact objContact : newContactMap.values())
		{
			if(objContact.AccountId != null)
				mapIdToAccountName.put(objContact.AccountId, null);
		}
	}
	
	// iterate over account with matching collected Ids and fetch names
	for(Account objAccount : [SELECT Id, Name FROM Account WHERE Id IN : mapIdToAccountName.keySet()])
	{
		mapIdToAccountName.put(objAccount.Id, objAccount.Name);
	}
	
	// loop again to perform business logic
	for(Contact objContact : newContactMap.values())
	{
		/* 1. If Contact RecordType is Customer AND if LastName != Account.Name, insert Queue **/
		if(	objContact.RecordTypeId == idContactCustomerRecType 
			&& objContact.AccountId != null 
			&& objContact.LastName != mapIdToAccountName.get(objContact.AccountId)
			(
				Trigger.isInsert
					||
				(
					Trigger.isUpdate
						&&
					(	
						objContact.A__c!= oldContactMap.get(objContact.Id).A__c 
						|| objContact.B__c!= oldContactMap.get(objContact.Id).B__c 
						|| objContact.D__c!= oldContactMap.get(objContact.Id).D__c
					)
				)
			)
			)
		{
			Queue__c objQ = new Queue__c();
			objQ.Object_Name__c='Contact';
			
			if(Trigger.isUpdate)
			{
				if(objContact.PID__c == null)
					objQ.Description__c= 'PID Missing';
				else
					objQ.Description__c= 'Updated Contact';
			}
			else 
			{
				objQ.Description__c= 'New Contact';
			}
			
			objQ.Record_Id__c = objContact.Id;
			objQ.Notification_Timestamp__c= objContact.CreatedDate;
			lstQ.add(objQ);
		}
			
	}
	
	if(!lstQ.isEmpty())
		insert lstQ;
	
}

 
Nayana KNayana K
Check if workflows, process builders are creating multiple records.
ABC XYZ 39ABC XYZ 39
Thanks for the reply Nayana. There are no workflows creating multiple records.

Can you pls explain in brief what you have changed in the code above? Thanks for all your help
ABC XYZ 39ABC XYZ 39
will processNewContact code have to be changed as well, Nayana?

Nayana, sorry, additonal logic which I forgot to include before is for update case should trigger only if specific users modify the record.  
Logic is :

                  if(label.Unauthorized_User!=l.lastmodifiedbyid){
    {  Resot of Update Logic }
}

Will above code have to be changed Nayana? Please let me know...Also, what would the test class for above piece of code look like? Thanks
ABC XYZ 39ABC XYZ 39
I am getting compile error: Expecting right parentheses, found '(' at line 48. I am not able to figure out what's the issue here. 
Nayana KNayana K
I have fixed line 48 issue, I was missing && operator there. Also added comments for you understanding
 
if(Trigger.isInsert && Trigger.isAfter)
{
	ContactController.processContact(Trigger.newMap, Trigger.oldMap);
}
else if(Trigger.isUpdate && Trigger.isAfter)
{
	ContactController.processContact(Trigger.newMap, Trigger.oldMap);
}



// call processContact on both insert and update
public  void processContact(Map<Id, Contact> newContactMap, Map<Id, Contact> oldContactMap) 
{ 
    List<Queue__c> lstQ = new List<Queue__c>();    
	Id idContactCustomerRecType;
	Map<Id, String> mapIdToAccountName = new Map<Id, String>();
	
	// Assuming Developer Name is 'Customer'
	List<RecordType> lstRecType = new List<RecordType>([SELECT Id FROM RecordType WHERE DeveloperName = 'Customer' AND SobjectType = 'Contact']);
	if(!lstRecType.isEmpty())
		idContactCustomerRecType = lstRecType[0].Id;
		
	if(idContactCustomerRecType != null)
	{
		// we cant fetch parent fields directly, so collect account Ids first
		for(Contact objContact : newContactMap.values())
		{
			if(objContact.AccountId != null)
				mapIdToAccountName.put(objContact.AccountId, null);
		}
	}
	
	// iterate over account with matching collected Ids and fetch names
	for(Account objAccount : [SELECT Id, Name FROM Account WHERE Id IN : mapIdToAccountName.keySet()])
	{
		mapIdToAccountName.put(objAccount.Id, objAccount.Name);
	}
	
	// loop again to perform business logic
	for(Contact objContact : newContactMap.values())
	{
		/* If Contact RecordType is Customer AND if LastName != Account.Name
			1. Enter into if block On Insert 
				OR
			2. Enter into if block On update but only if label.Unauthorized_User != objContact.lastmodifiedbyid AND any of the field values among A,B,C are changed 
		**/
		if(	objContact.RecordTypeId == idContactCustomerRecType 
			&& objContact.AccountId != null 
			&& objContact.LastName != mapIdToAccountName.get(objContact.AccountId)
			&&
			(
				Trigger.isInsert
					||
				(
					Trigger.isUpdate
						&&
					label.Unauthorized_User != objContact.lastmodifiedbyid
					(	
						objContact.A__c!= oldContactMap.get(objContact.Id).A__c 
						|| objContact.B__c!= oldContactMap.get(objContact.Id).B__c 
						|| objContact.D__c!= oldContactMap.get(objContact.Id).D__c
					)
				)
			)
			)
		{
			Queue__c objQ = new Queue__c();
			objQ.Object_Name__c='Contact';
			
			// On update
			if(Trigger.isUpdate)
			{
				// if PID = null then Description__c = 'PID Missing'
				if(objContact.PID__c == null)
					objQ.Description__c= 'PID Missing';
				// if PID != null then Description__c = 'Updated Contact'
				else
					objQ.Description__c= 'Updated Contact';
			}
			// On insert ,Description__c = 'New Contact'
			else 
			{
				objQ.Description__c= 'New Contact';
			}
			
			objQ.Record_Id__c = objContact.Id;
			objQ.Notification_Timestamp__c= objContact.CreatedDate;
			lstQ.add(objQ);
		}
			
	}
	
	if(!lstQ.isEmpty())
		insert lstQ;
	
}

Have you checked process builder? 

Please post whole trigger code and controller code ( just to make sure everything is proper).
ABC XYZ 39ABC XYZ 39
Thank you for your response, Nayana

Here is the code:


// Controller Class with processContact method

public class ContactController {

public  void processContact(Map<Id, Contact> newContactMap, Map<Id, Contact> oldContactMap)
{
       List<Queue__c> lstQ = new List<Queue__c>();   
         Id idContactCustomerRecType;
         Map<Id, String> mapIdToAccountName = new Map<Id, String>();
       
           List<RecordType> lstRecType = new List<RecordType>([SELECT Id FROM RecordType WHERE Name = 'Customer' AND SobjectType = 'Contact']);
          
      if(!lstRecType.isEmpty())
     idContactCustomerRecType = lstRecType[0].Id;
       
        if(idContactCustomerRecType != null)
         {
         // we can't fetch parent fields directly, so collecting account Ids first
           for(Contact objContact : newContactMap.values())
              {
                  if(objContact.AccountId != null)
                mapIdToAccountName.put(objContact.AccountId, null);
              }
          }
 
         // iterate over account with matching collected Ids and fetch names
          for(Account objAccount : [SELECT Id, Name FROM Account WHERE Id IN : mapIdToAccountName.keySet()])
          {
           mapIdToAccountName.put(objAccount.Id, objAccount.Name);
           }
  
         // loop again to perform business logic
        for(Contact objContact : newContactMap.values())
        {
             /* 1. If Contact RecordType is Customer AND if LastName != Account.Name, insert Queue **/
            if( objContact.RecordTypeId == idContactCustomerRecType
               && objContact.AccountId != null   && objContact.LastName != mapIdToAccountName.get(objContact.AccountId)
             {
             if(Trigger.isUpdate)
             {
                 if(label.Unauthorized_User!=objContact.lastmodifiedbyid)
                 {
                    if(objContact.A__c!= oldContactMap.get(objContact.Id).A__c
                        || objContact.B__c!= oldContactMap.get(objContact.Id).B__c
                        || objContact.D__c!= oldContactMap.get(objContact.Id).D__c)
                  {
                  
                   objQ.Description__c= 'Updated Contact';
                   objQ.Record_Id__c = objContact.Id;
                   objQ.Notification_Timestamp__c= objContact.CreatedDate;
                   lstQ.add(objQ);
                   }
                   
                   else if(objContact.PID__c == null)
                   {
                   objQ.Description__c= 'PID Missing';
                   objQ.Record_Id__c = objContact.Id;
                   objQ.Notification_Timestamp__c= objContact.CreatedDate;
                   lstQ.add(objQ);
                   
                   }
                   
                 }
             }
             
               if(Trigger.isInsert)
               {
                 objQ.Description__c= 'New Contact';
                   objQ.Record_Id__c = objContact.Id;
                   objQ.Notification_Timestamp__c= objContact.CreatedDate;
                   lstQ.add(objQ);
                   
               }
             }

            
                   
                
       }

       if(!lstQ.isEmpty())
       insert lstQ;
             
    }
     
    
     
}


Trigger Invocation :

Contact - After Insert 

if(Trigger.isInsert && Trigger.isAfter){
   ContactController.processContact(Trigger.newMap, Trigger.oldMap);
   }

Contact - After Update 

 if(Trigger.isUpdate && Trigger.isAfter){
        ContactController.processContact(Trigger.newMap, Trigger.oldMap);
    }