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
Roshan 10Roshan 10 

passing List<Contact> from Trigger to a class but not able to update the record (Before Triggers and Queueable APEX)

I am trying to create a trigger on Contact. When a custom field Company_Name__c on Contact is populated the trigger should create an Account if it does not exist. Also the AccountId should be updated on the Contact record
I was able to do it with simple trigger code but noticed that there is a limit (Number of Query Rows: 50000). So I started trying using Queueable APEX but ran into this issue
I am not able to update the Contact records. I see the AccountId value in the debug logs but the Contact record is not updated with AccountId after the Trigger completed
Can you suggest how this can be achieved

Below is the code I am using

CreateAccountFromCompanyTrigger

trigger CreateAccountFromCompanyTrigger on Contact (before insert, after insert) {
    If(Trigger.isBefore && Trigger.isInsert)
    {
       
    //ContactTriggerClass.InsertContacts(Trigger.new); 
    ContactTriggerClass CreateContactJob = new ContactTriggerClass(Trigger.new);     
    System.enqueueJob(CreateContactJob);
               
    }//If
}

ContactTriggerClass

public class ContactTriggerClass implements Queueable  {
  
    //public List<Contact> TriggeredContactList = new List<Contact>();
    public List<Contact> TriggeredContactList;
   
    public ContactTriggerClass(List<Contact> ContactList){
        TriggeredContactList = ContactList;
       
    }//mainmethod
   
    public void execute(QueueableContext context) {
    List<Account> AcctsToCreate = new List<Account>();
    Map<String, Account> AllAccountsMap = new Map<String, Account>();
       
        for(Account acct : [Select Id, Name from Account]){
            AllAccountsMap.put(acct.Name, acct);
           
        }
   
    System.debug('AllAccountsMap:' +AllAccountsMap.size());
       
        Set<String> CompanyNameSet = new Set<String>();
        for(Contact cntct : TriggeredContactList ){
            if(!AllAccountsMap.containsKey(cntct.Company_Name__c))
                CompanyNameSet.add(cntct.Company_Name__c);
        }   
       
         if(CompanyNameSet.size() > 0){
            for(String CompanyName : CompanyNameSet)
            AcctsToCreate.add(new Account(Name = CompanyName));
        insert AcctsToCreate;
        }
       
        if(AcctsToCreate.size() > 0)
        {
            for(Account acct : AcctsToCreate)
                AllAccountsMap.put(acct.Name, acct);
        }
        System.debug('UpdatedAccountsMap' +AllAccountsMap.size()); 
       
        for(Contact cntct : TriggeredContactList){
            cntct.AccountId = AllAccountsMap.get(cntct.Company_Name__c).Id;
            System.debug('cntct record:' +cntct +cntct.AccountId);
        }
       
       
}//execute
  
   
   
}//class
 
Santosh Reddy9989Santosh Reddy9989
If you call Queueable APEX  then it will seperately executed, So u need to update contact manually. For this one you have to Put Update statement for contact in Queueable APEX
Santosh Reddy9989Santosh Reddy9989
for(Contact cntct : TriggeredContactList){
            cntct.AccountId = AllAccountsMap.get(cntct.Company_Name__c).Id;
            System.debug('cntct record:' +cntct +cntct.AccountId);
        }

Update TriggeredContactList ;
Roshan 10Roshan 10
Tried the above code but getting an error
 Update failed. First exception on row 0; first error: MISSING_ARGUMENT, Id not specified in an update call: []
Santosh Reddy9989Santosh Reddy9989
Change the trigger code for after insert :

trigger CreateAccountFromCompanyTrigger on Contact (before insert, after insert) {
    If( Trigger.isAfter && Trigger.isInsert)
    {
       
    //ContactTriggerClass.InsertContacts(Trigger.new); 
    ContactTriggerClass CreateContactJob = new ContactTriggerClass(Trigger.new);     
    System.enqueueJob(CreateContactJob);
               
    }//If
}
Roshan 10Roshan 10
Santosh....Insert is working fine and thank you for that. I am trying to do an Update also but the code is running recurrently. Can you take a look at the Update also. Below is the code I used

CreateAccountFromCompanyTrigger

trigger CreateAccountFromCompanyTrigger on Contact (after update) {
   
    If(ContactTriggerClass.isFirstTime == true){
       ContactTriggerClass.isFirstTime = false;   
   
    If(Trigger.isAfter){
       If (Trigger.isUpdate)
    {
   
    //ContactTriggerClass.InsertContacts(Trigger.new); 
    ContactTriggerClass CreateContactJob = new ContactTriggerClass(Trigger.new, Trigger.newMap);     
   
        Id jobId = System.enqueueJob(CreateContactJob);
        AsyncApexJob jobInfo = [Select Status, NumberOfErrors from AsyncApexJob where Id = :jobId];
   
   
    }
    }//If
    }//If AlreadyProcessed
   
}//trigger

ContactTriggerClass

public class ContactTriggerClass implements Queueable  {
   
    public static boolean isFirstTime = true;
    public List<Contact> TriggeredContactList = new List<Contact>();
   
    public Set<Id> TriggeredContactIds;
   
    public ContactTriggerClass(List<Contact> ContactList, Map<Id, Contact> ContactMap){
       
        TriggeredContactList = [Select Id, Name, Company_Name__c from Contact where Id in :ContactMap.keySet() and Company_Name__c != null];
        TriggeredContactIds = ContactMap.keySet();
       
    }//mainmethod
   
    public void execute(QueueableContext context) {
    List<Account> AcctsToCreate = new List<Account>();
    Map<String, Account> AllAccountsMap = new Map<String, Account>();
   
        if(TriggeredContactList.size() > 0){
   
        for(Account acct : [Select Id, Name from Account]){
            AllAccountsMap.put(acct.Name, acct);
           
        }
   
    System.debug('AllAccountsMap:' +AllAccountsMap.size());
       
        Set<String> CompanyNameSet = new Set<String>();
        for(Contact cntct : TriggeredContactList ){
            if(!AllAccountsMap.containsKey(cntct.Company_Name__c))
                CompanyNameSet.add(cntct.Company_Name__c);
        }   
       
         if(CompanyNameSet.size() > 0){
            for(String CompanyName : CompanyNameSet)
            AcctsToCreate.add(new Account(Name = CompanyName));
        insert AcctsToCreate;
        }
       
        if(AcctsToCreate.size() > 0)
        {
            for(Account acct : AcctsToCreate)
                AllAccountsMap.put(acct.Name, acct);
        }
   
       
        List<Contact> UpdatedTriggerContactList = [Select Id, Name, Company_Name__c from Contact where Id in :TriggeredContactIds];
       
       
        for(Contact cntct : UpdatedTriggerContactList){
            cntct.AccountId = AllAccountsMap.get(cntct.Company_Name__c).Id;
            System.debug('cntct record:' +cntct +cntct.AccountId);
   
        }
   
        Update UpdatedTriggerContactList;
        }//If TriggeredContactList.size
}//execute
  
   
   
}//class
Santosh Reddy9989Santosh Reddy9989
write recursive checking class seperately then it will work.
CreateAccountFromCompanyTrigger :

trigger CreateAccountFromCompanyTrigger on Contact (after update)
{
   
    If(checkAccountRecursive.runAfterUpdateOnce())
  {
     
   
    If(Trigger.isAfter){
       If (Trigger.isUpdate)
    {
   
    //ContactTriggerClass.InsertContacts(Trigger.new); 
    ContactTriggerClass CreateContactJob = new ContactTriggerClass(Trigger.new, Trigger.newMap);     
   
        Id jobId = System.enqueueJob(CreateContactJob);
        AsyncApexJob jobInfo = [Select Status, NumberOfErrors from AsyncApexJob where Id = :jobId];
   
   
    }
    }//If
    }//If AlreadyProcessed
   
}//trigger


ContactTriggerClass :

public class ContactTriggerClass implements Queueable  
{
  

    public List<Contact> TriggeredContactList = new List<Contact>();
   
    public Set<Id> TriggeredContactIds;
   
    public ContactTriggerClass(List<Contact> ContactList, Map<Id, Contact> ContactMap){
       
        TriggeredContactList = [Select Id, Name, Company_Name__c from Contact where Id in :ContactMap.keySet() and Company_Name__c != null];
        TriggeredContactIds = ContactMap.keySet();
       
    }//mainmethod
   
    public void execute(QueueableContext context) {
    List<Account> AcctsToCreate = new List<Account>();
    Map<String, Account> AllAccountsMap = new Map<String, Account>();
   
        if(TriggeredContactList.size() > 0){
   
        for(Account acct : [Select Id, Name from Account]){
            AllAccountsMap.put(acct.Name, acct);
           
        }
   
    System.debug('AllAccountsMap:' +AllAccountsMap.size());
       
        Set<String> CompanyNameSet = new Set<String>();
        for(Contact cntct : TriggeredContactList ){
            if(!AllAccountsMap.containsKey(cntct.Company_Name__c))
                CompanyNameSet.add(cntct.Company_Name__c);
        }   
       
         if(CompanyNameSet.size() > 0){
            for(String CompanyName : CompanyNameSet)
            AcctsToCreate.add(new Account(Name = CompanyName));
        insert AcctsToCreate;
        }
       
        if(AcctsToCreate.size() > 0)
        {
            for(Account acct : AcctsToCreate)
                AllAccountsMap.put(acct.Name, acct);
        }
   
       
        List<Contact> UpdatedTriggerContactList = [Select Id, Name, Company_Name__c from Contact where Id in :TriggeredContactIds];
       
       
        for(Contact cntct : UpdatedTriggerContactList){
            cntct.AccountId = AllAccountsMap.get(cntct.Company_Name__c).Id;
            System.debug('cntct record:' +cntct +cntct.AccountId);
   
        }
   
        Update UpdatedTriggerContactList;
        }//If TriggeredContactList.size
}//execute
 
   
   
}//class

checkAccountRecursive :

public class checkAccountRecursive
{
     public static boolean isFirstTime = true;

    public static boolean runAfterUpdateOnce()
    {
        
    if(isFirstTime)
    {
     isFirstTime = false;
     return true;   
    }
    else
    {
        return isFirstTime;
    }
        
    }
}


Thanks,
Santosh Reddy
Roshan 10Roshan 10
Santosh...it is still running recursively. I also tried creating a brand new developer org and running this code

 
Santosh Reddy9989Santosh Reddy9989
Hi, try this class.

ContactTriggerClass :

public class ContactTriggerClass implements Queueable  
{
  

    public List<Contact> TriggeredContactList = new List<Contact>();
   
    public Set<Id> TriggeredContactIds;
   
    public ContactTriggerClass(List<Contact> ContactList, Map<Id, Contact> ContactMap){
       
        TriggeredContactList = [Select Id, Name, Company_Name__c from Contact where Id in :ContactMap.keySet() and Company_Name__c != null];
        TriggeredContactIds = ContactMap.keySet();
       
    }//mainmethod
   
    public void execute(QueueableContext context) {
    List<Account> AcctsToCreate = new List<Account>();
    Map<String, Account> AllAccountsMap = new Map<String, Account>();
   
        if(TriggeredContactList.size() > 0){
   
        for(Account acct : [Select Id, Name from Account]){
            AllAccountsMap.put(acct.Name, acct);
           
        }
   
    System.debug('AllAccountsMap:' +AllAccountsMap.size());
       
        Set<String> CompanyNameSet = new Set<String>();
        for(Contact cntct : TriggeredContactList ){
            if(!AllAccountsMap.containsKey(cntct.Company_Name__c))
                CompanyNameSet.add(cntct.Company_Name__c);
        }   
       
         if(CompanyNameSet.size() > 0){
            for(String CompanyName : CompanyNameSet)
            AcctsToCreate.add(new Account(Name = CompanyName));
        insert AcctsToCreate;
        }
       
        if(AcctsToCreate.size() > 0)
        {
            for(Account acct : AcctsToCreate)
                AllAccountsMap.put(acct.Name, acct);
        }
   
       
        List<Contact> UpdatedTriggerContactList = [Select Id, Name, Company_Name__c from Contact where Id in :TriggeredContactIds];
       
       
        for(Contact cntct : UpdatedTriggerContactList){
            cntct.AccountId = AllAccountsMap.get(cntct.Company_Name__c).Id;
            System.debug('cntct record:' +cntct +cntct.AccountId);
   
        }

   checkAccountRecursive.isFirstTime  = false;

        Update UpdatedTriggerContactList;
        }//If TriggeredContactList.size
}//execute
 
   
   
}//class

Hope this helps, mark this as solved.

Thanks,
Santosh Reddy
 
Roshan 10Roshan 10
Santosh,

The code worked fine for 1 batch of records but when I tried with 230 records only the first 200 are processed correctly

Thanks,
Roshan
 
Santosh Reddy9989Santosh Reddy9989
checkAccountRecursive.isFirstTime  = true;
 place the above statement in execute method of batch apex as a 1st line and try.

Thanks
Santosh Reddy
Matthew CoolidgeMatthew Coolidge

@Santosh (and whomever may read this after 4 years....)

That is the problem with using a single static variable to control trigger re-entry/recursion. The platform inherently "batches" records flowing into a trigger context into 200 record chunks and the trigger fires for each chunk. But, if your static was set for the first chunk, none of the subsequent ones will be processed.

A better strategy is to maintain a set<id> of records that have been updated and filter your code execution based on that. Obviouly that won't work for INSERT, because they dont have ids yet..... but INSERT doesnt really recurse so it's not actually a problem.