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
DipthiDipthi 

Plz correct my code - My code doesn't work when account is modified on contact

To show total no of contact records on Account

trigger ContactsTotalDisplayOnAccount on Contact (after insert, after update, after delete, after undelete) {
//Contact can be added/deleted/undeleted

List<contact> contacts = Trigger.isDelete ? Trigger.old : Trigger.new;
set<Id> accId = new set<Id>();
// Set is used to store unique values
    
for(contact con : contacts) {
    If(con.AccountId != null) {
       accId.add(con.AccountId); 
    }    
}
List<account> acclist = new List<account>();

for(account acc : [Select Id,Name,Total_Number_of_Contacts__c,(Select Id from Contacts)
                   from Account 
                   where Id IN :accId]) {
    account acts = new Account();
    acts.Id = acc.Id ; 
    acts.Total_Number_of_Contacts__c = acc.contacts.size(); 
    acclist.add(acts);                   
}
update acclist;
}

 
Best Answer chosen by Dipthi
Ajay MohananAjay Mohanan
The code is missing logic to reduce count from old account when account is switching on the contact. 
Based on these lines while in update trigger, your accId set is built from Trigger.New, which will only have new AccountIds.

List<contact> contacts = Trigger.isDelete ? Trigger.old : Trigger.new;
set<Id> accId = new set<Id>();
// Set is used to store unique values
    
for(contact con : contacts) {
    If(con.AccountId != null) {
       accId.add(con.AccountId); 
    }    
}

While I liked the cleaner approach by CharuDutt, Delete and Insert will work without no problem, but update has the same problem. 

     if(trigger.isUpdate){
        if(trigger.isAfter){
            for(contact lstCon : trigger.new){
               lst.add(lstcon.AccountId); 
            }
        }
    }
    
    
A quick fix would be to add another loop with trigger.old and add accountIds from that list as well to refresh the count on the old accounts.
     if(trigger.isUpdate){
        if(trigger.isAfter){
            for(contact lstCon : trigger.new){
               lst.add(lstcon.AccountId); 
            }
            for(contact lstCon : trigger.old){
               lst.add(lstcon.AccountId); 
            }

        }
    }    
  

I would also add an additional check to see if accountId is updated, to limit your final update logic to only those record with accountId updates.
Here is the updated logic with only one loop and AccountId check.
     if(trigger.isUpdate){
        if(trigger.isAfter){
            for(contact lstCon : trigger.new){
                if(lstcon.AccountId != Trigger.oldMap.get(lstCon.Id).AccountId){
                    //AccountId Updated 
                    //Add new AccountId to recalculate count
                    lst.add(lstcon.AccountId); 
                    //Add old AccountId to recalculate count
                    lst.add(Trigger.oldMap.get(lstCon.Id).AccountId);
                }
            }
        }
    } 

Note: I did not execute the code. So there might be some typo or syntax errors. 

All Answers

CharuDuttCharuDutt
Hii Dipthi
Try the following code 
trigger NumberOfChild on Contact (After insert,After Update,after delete) {
  set<id> lst = new set <id>();
    if(trigger.isInsert){
        if(trigger.isAfter){
            for(contact lstCon : trigger.new){
               lst.add(lstcon.AccountId); 
            }
        }
    }
     if(trigger.isUpdate){
        if(trigger.isAfter){
            for(contact lstCon : trigger.new){
               lst.add(lstcon.AccountId); 
            }
        }
    }
     if(trigger.isDelete){
        if(trigger.isAfter){
            for(contact lstCon : trigger.old){
               lst.add(lstcon.AccountId); 
            }
        }
    }
     List<Account> listAccs = [Select id,name,Total_Number_of_Contacts__c ,(Select id from contacts) from Account where Id in :lst];
  for(Account acc :listAccs)
  {
   acc.Total_Number_of_Contacts__c = acc.contacts.size();
  }
  update listAccs;
}
Please Mark it As Best Answer if It Helps
Thank you!
 
DipthiDipthi
Hi CharuDutt,
Thank u for ur response. Your code is all good when:
- contact is inserted/associated to account
- contact is deleted and undeleted

But, the problem I mentioned with my code stille exists. Ex: I have contact 'xyz' associated with 'account 'abc'  ->  I go to 'xyz' contact and change the account from 'xyz' to any other account  -> 'Total child contacts' field shows no change on abc' account 
Ajay MohananAjay Mohanan
The code is missing logic to reduce count from old account when account is switching on the contact. 
Based on these lines while in update trigger, your accId set is built from Trigger.New, which will only have new AccountIds.

List<contact> contacts = Trigger.isDelete ? Trigger.old : Trigger.new;
set<Id> accId = new set<Id>();
// Set is used to store unique values
    
for(contact con : contacts) {
    If(con.AccountId != null) {
       accId.add(con.AccountId); 
    }    
}

While I liked the cleaner approach by CharuDutt, Delete and Insert will work without no problem, but update has the same problem. 

     if(trigger.isUpdate){
        if(trigger.isAfter){
            for(contact lstCon : trigger.new){
               lst.add(lstcon.AccountId); 
            }
        }
    }
    
    
A quick fix would be to add another loop with trigger.old and add accountIds from that list as well to refresh the count on the old accounts.
     if(trigger.isUpdate){
        if(trigger.isAfter){
            for(contact lstCon : trigger.new){
               lst.add(lstcon.AccountId); 
            }
            for(contact lstCon : trigger.old){
               lst.add(lstcon.AccountId); 
            }

        }
    }    
  

I would also add an additional check to see if accountId is updated, to limit your final update logic to only those record with accountId updates.
Here is the updated logic with only one loop and AccountId check.
     if(trigger.isUpdate){
        if(trigger.isAfter){
            for(contact lstCon : trigger.new){
                if(lstcon.AccountId != Trigger.oldMap.get(lstCon.Id).AccountId){
                    //AccountId Updated 
                    //Add new AccountId to recalculate count
                    lst.add(lstcon.AccountId); 
                    //Add old AccountId to recalculate count
                    lst.add(Trigger.oldMap.get(lstCon.Id).AccountId);
                }
            }
        }
    } 

Note: I did not execute the code. So there might be some typo or syntax errors. 
This was selected as the best answer
Malika Pathak 9Malika Pathak 9

Hi Dipthi,

Try this code

 

List<contact> contacts = Trigger.isDelete ? Trigger.old : Trigger.new;
set<Id> accId = new set<Id>();
// Set is used to store unique values
    
for(contact con : contacts) {
    If(con.AccountId != null) {
       accId.add(con.AccountId); 
    }    
}
List<account> acclist = new List<account>();

fList<account> acclISt1=[Select Id,Name,Total_Number_of_Contacts__c,(Select Id from Contacts)
                   from Account 
                   where Id IN :accId Limit 10000];
 
 for(account acc :  acclISt1 {
    account acts = new Account();
    acts.Id = acc.Id ; 
    acts.Total_Number_of_Contacts__c = acc.contacts.size(); 
    acclist.add(acts);                   
}
update acclist;
}

if you find this helpful mark it as the best answer.
DipthiDipthi
Thank you very much Ajay Mohanan, Charu Dutt & Malika