You need to sign in to do that
Don't have an account?
Roshan 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
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
cntct.AccountId = AllAccountsMap.get(cntct.Company_Name__c).Id;
System.debug('cntct record:' +cntct +cntct.AccountId);
}
Update TriggeredContactList ;
Update failed. First exception on row 0; first error: MISSING_ARGUMENT, Id not specified in an update call: []
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
}
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
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
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
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
place the above statement in execute method of batch apex as a 1st line and try.
Thanks
Santosh Reddy
@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.