+ Start a Discussion
shobana shobana 1shobana shobana 1 

How to perform trigge update operation on exiting records

I wrote a trigger which will count the contact and update in Accout field(Number_of_Contacts__c). But trigger will only work when i create a record in contact.But i want to do updation on existing records too. Can anyone help me out to solve this issue.

trigger ContactCount on Contact (after insert, after update, after delete, after undelete) {
    Map<Id, List<Contact>> mapAcctIdContactList = new Map<Id, List<Contact>>();
    Map<Id, List<Contact>> mapAcctIdDelContactList = new Map<Id, List<Contact>>();
    Set<Id> AcctIds = new Set<Id>();    
    List<Account> listAcct = new List<Account>();
    
    if(trigger.isInsert) {
        for(Contact Con : trigger.New) {
            if(String.isNotBlank(Con.AccountId)) {
                if(!mapAcctIdContactList.containsKey(Con.AccountId)) {
                    mapAcctIdContactList.put(Con.AccountId, new List<Contact>());
                }
                mapAcctIdContactList.get(Con.AccountId).add(Con); 
                AcctIds.add(Con.AccountId);
            }   
        }  
    }
    
    if(trigger.isUpdate) {
        for(Contact Con : trigger.New) {
            if(String.isNotBlank(Con.AccountId) && Con.AccountId != trigger.oldMap.get(Con.Id).AccountId) {
                if(!mapAcctIdContactList.containsKey(Con.AccountId)){
                    mapAcctIdContactList.put(Con.AccountId, new List<Contact>());
                }
                mapAcctIdContactList.get(Con.AccountId).add(Con); 
                AcctIds.add(Con.AccountId);
            } else if(String.isBlank(Con.AccountId) && String.isNotBlank(trigger.oldMap.get(Con.Id).AccountId)) {
                if(!mapAcctIdDelContactList.containsKey(Con.AccountId)){
                    mapAcctIdDelContactList.put(Con.AccountId, new List<Contact>());
                }
                mapAcctIdDelContactList.get(Con.AccountId).add(Con);   
                AcctIds.add(trigger.oldMap.get(Con.Id).AccountId);
            }
        }  
    }
    
    if(trigger.isUndelete) {
        for(Contact Con : trigger.new) {
            if(String.isNotBlank(Con.AccountId)){
                if(!mapAcctIdContactList.containsKey(Con.AccountId)){
                    mapAcctIdContactList.put(Con.AccountId, new List<Contact>());
                }
                mapAcctIdContactList.get(Con.AccountId).add(Con);     
                AcctIds.add(Con.AccountId);
            }
        }  
    }      

    if(trigger.isDelete) {
        for(Contact Con : trigger.Old) {
            if(String.isNotBlank(Con.AccountId)){
                if(!mapAcctIdDelContactList.containsKey(Con.AccountId)){
                    mapAcctIdDelContactList.put(Con.AccountId, new List<Contact>());
                }
                mapAcctIdDelContactList.get(Con.AccountId).add(Con);    
                AcctIds.add(Con.AccountId); 
            }
        }  
    }   
    
    if(AcctIds.size() > 0) {
        listAcct = [SELECT Id, Number_of_Contacts__c FROM Account WHERE Id IN : AcctIds];
        
        for(Account acct : listAcct) {
            Integer noOfConts = 0;
            if(mapAcctIdContactList.containsKey(acct.Id)) {
                noOfConts += mapAcctIdContactList.get(acct.Id).size();
            }
            if(mapAcctIdDelContactList.containsKey(acct.Id)) {
                noOfConts -= mapAcctIdDelContactList.get(acct.Id).size();
            }
            acct.Number_of_Contacts__c = acct.Number_of_Contacts__c == null ? noOfConts : (acct.Number_of_Contacts__c + noOfConts);
        }
        
        update listAcct;    
    }
}
Best Answer chosen by shobana shobana 1
Mahesh DMahesh D
Hi Shobana,

There is only one option that is, you have to modify the code to execute it in the Batch Apex.

Dummy update will not work because, here if you see the code
 
if(String.isNotBlank(Con.AccountId) && Con.AccountId != trigger.oldMap.get(Con.Id).AccountId) {
This will not execute as we will not update the Account as part of dummy update.

Please check the below code which will do perform your requirement:
 
global class CalculateContactBatch implements Database.Batchable<sObject>, Schedulable, Database.Stateful {

    //Variable Section
    global FINAL String strQuery;
    global List<String> errorMessages = new List<String>();
    
    global CalculateContactBatch() { 
        this.strQuery = getBatchQuery();
    }
    
    //Returns the Query String to Batch constructor to fetch right records.
    private String getBatchQuery() {
        String strQuery = 'Select Id, Number_of_Contacts__c, (Select Id From Contacts) From Account'; 
        return strQuery;
    }
    
    //Batch Start method
    global Database.QueryLocator start(Database.BatchableContext BC) {
        return Database.getQueryLocator(strQuery);
    }

    //Batch Execute method calls findCostForWoD method
    global void execute(Database.BatchableContext BC, List<sObject> scopeList) {
        System.debug(LoggingLevel.INFO, '== scopeList size ==' + scopeList.size());
        
        List<Account> accList = (List<Account>) scopeList;
            
        for(Account acc: accList) {
            acc.Number_of_Contacts__c = acc.Contacts.size();
        }
        
        
        try {                    
            Database.SaveResult[] saveResults = Database.update(accList, false);
            for (Database.SaveResult sr : SaveResults) {
                if(!sr.isSuccess()) {
                    for (Database.Error err : sr.getErrors()) {
                        errorMessages.add('Error: ' + err.getStatusCode() + ': ' + err.getMessage());
                    }
                    system.debug(sr.getErrors()[0].getMessage());
                }
            }
            System.debug('errorMessages: '+errorMessages);
        } catch (System.Exception ex) {
            System.debug('An error occurred when updating Contact Calculation Batch: ' + ex.getMessage());
            return;
        }
    }  

    //Batch Finish method for after execution of batch work
    global void finish(Database.BatchableContext BC) { 
        AsyncApexJob aaj = [Select Id, Status, NumberOfErrors, JobItemsProcessed, MethodName, TotalJobItems, CreatedBy.Email from AsyncApexJob where Id =:BC.getJobId()];
        
        // Send an email to the Apex job's submitter notifying of job completion.
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        String[] toAddresses = new String[] {aaj.CreatedBy.Email};
        mail.setToAddresses(toAddresses);
        mail.setSubject('JOB Salesforce CalculateContactBatch Finished: ' + aaj.Status);
        String bodyText='Total Job Items ' + aaj.TotalJobItems + ' Number of records processed ' + aaj.JobItemsProcessed + ' with '+ aaj.NumberOfErrors + ' failures.\n';
        bodyText += 'Number of Error Messages ' + errorMessages.size() + '\n';
        bodyText += 'Error Message' + String.join(errorMessages, '\n');
        mail.setPlainTextBody(bodyText);
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }
    
    //Method which schedules the CalculateContactBatch 
    global void execute(SchedulableContext sc) {
        CalculateContactBatch ccb= new CalculateContactBatch();
        ID batchprocessid = Database.executeBatch(ccb);
    }
}

Please do let me know if it helps you.

Regards,
Mahesh


 

All Answers

Shailendra Singh ParmarShailendra Singh Parmar
You either need to dummy update all contacts once or create batch job that will update accounts for that rollup fields.
Mahesh DMahesh D
Hi Shobana,

There is only one option that is, you have to modify the code to execute it in the Batch Apex.

Dummy update will not work because, here if you see the code
 
if(String.isNotBlank(Con.AccountId) && Con.AccountId != trigger.oldMap.get(Con.Id).AccountId) {
This will not execute as we will not update the Account as part of dummy update.

Please check the below code which will do perform your requirement:
 
global class CalculateContactBatch implements Database.Batchable<sObject>, Schedulable, Database.Stateful {

    //Variable Section
    global FINAL String strQuery;
    global List<String> errorMessages = new List<String>();
    
    global CalculateContactBatch() { 
        this.strQuery = getBatchQuery();
    }
    
    //Returns the Query String to Batch constructor to fetch right records.
    private String getBatchQuery() {
        String strQuery = 'Select Id, Number_of_Contacts__c, (Select Id From Contacts) From Account'; 
        return strQuery;
    }
    
    //Batch Start method
    global Database.QueryLocator start(Database.BatchableContext BC) {
        return Database.getQueryLocator(strQuery);
    }

    //Batch Execute method calls findCostForWoD method
    global void execute(Database.BatchableContext BC, List<sObject> scopeList) {
        System.debug(LoggingLevel.INFO, '== scopeList size ==' + scopeList.size());
        
        List<Account> accList = (List<Account>) scopeList;
            
        for(Account acc: accList) {
            acc.Number_of_Contacts__c = acc.Contacts.size();
        }
        
        
        try {                    
            Database.SaveResult[] saveResults = Database.update(accList, false);
            for (Database.SaveResult sr : SaveResults) {
                if(!sr.isSuccess()) {
                    for (Database.Error err : sr.getErrors()) {
                        errorMessages.add('Error: ' + err.getStatusCode() + ': ' + err.getMessage());
                    }
                    system.debug(sr.getErrors()[0].getMessage());
                }
            }
            System.debug('errorMessages: '+errorMessages);
        } catch (System.Exception ex) {
            System.debug('An error occurred when updating Contact Calculation Batch: ' + ex.getMessage());
            return;
        }
    }  

    //Batch Finish method for after execution of batch work
    global void finish(Database.BatchableContext BC) { 
        AsyncApexJob aaj = [Select Id, Status, NumberOfErrors, JobItemsProcessed, MethodName, TotalJobItems, CreatedBy.Email from AsyncApexJob where Id =:BC.getJobId()];
        
        // Send an email to the Apex job's submitter notifying of job completion.
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        String[] toAddresses = new String[] {aaj.CreatedBy.Email};
        mail.setToAddresses(toAddresses);
        mail.setSubject('JOB Salesforce CalculateContactBatch Finished: ' + aaj.Status);
        String bodyText='Total Job Items ' + aaj.TotalJobItems + ' Number of records processed ' + aaj.JobItemsProcessed + ' with '+ aaj.NumberOfErrors + ' failures.\n';
        bodyText += 'Number of Error Messages ' + errorMessages.size() + '\n';
        bodyText += 'Error Message' + String.join(errorMessages, '\n');
        mail.setPlainTextBody(bodyText);
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }
    
    //Method which schedules the CalculateContactBatch 
    global void execute(SchedulableContext sc) {
        CalculateContactBatch ccb= new CalculateContactBatch();
        ID batchprocessid = Database.executeBatch(ccb);
    }
}

Please do let me know if it helps you.

Regards,
Mahesh


 
This was selected as the best answer
shobana shobana 1shobana shobana 1
Hi Mahesh

Thank you for your reply. In account its not updating the value.
Mahesh DMahesh D
Hi Shobana,

I am not sure how you are testing the code.

Go to Developer Console and execute the below 2 lines:

 
CalculateContactBatch ccb= new CalculateContactBatch();
ID batchprocessid = Database.executeBatch(ccb);

I tested the above code and it is working in my DE environment.

Please do let me know if it helps you.

Regards,
Mahesh
shobana shobana 1shobana shobana 1
Hi Mahesh
 Thank you for your reply. I did mistake now its working fine.