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
Jacob Elliott 1Jacob Elliott 1 

Batch Method that updates a field on contacts when an account is changed isn't working correctly

I have a class that will update a field, Last_Changed_Date, on Contacts if certain fields on the Account are changed. This class worked but was touching so many records it timed out. My solution was to pass all the changes that needed to be made to Contact and pass a list to a Batch Method. Unfortunately, since adding the batch method it doesn't seem to be working. Any thoughts would be really appreciated!

Below is the main class
public with sharing class CCIProviderChangeAccountTracking {

    public static void resaveContact(Account oldAcct, Account newAcct) {

        if (oldAcct.Phone != newAcct.Phone ||
                oldAcct.ShippingAddress != newAcct.ShippingAddress ||
                oldAcct.V12C__PR_Practice_Tax_ID_Number__c != newAcct.V12C__PR_Practice_Tax_ID_Number__c ||
                oldAcct.BillingAddress != newAcct.BillingAddress) {
            system.debug('------Conditions Met!!!!!!!!!------');
            List<Contact> toSave = new List <Contact>();
            for (Contact con : [SELECT Id, Last_Changed_Date__c, AccountId FROM Contact WHERE AccountId = :newAcct.Id]) {
                //con.Last_Changed_Date__c = Datetime.NOW();
                toSave.add(con);
            }
            system.debug('jacob-------------' + toSave.size());

            try {
                Id batchInstanceId = Database.executeBatch(new CCIAllegianceUpdates(toSave), 200);
            } catch (exception e) {
                system.debug(e.getMessage());
            }
        }
    }
}

Here is the batch method that is being passed the list toSave.
 
global class CCIAllegianceUpdates implements Database.Batchable<sObject> {

    List<Contact> toSave = new List <Contact>();
    public CCIAllegianceUpdates(List<Contact> toSave) {
        toSave = toSave;
    }

    global Database.QueryLocator start(Database.BatchableContext BC) {
        system.debug('------Starting Batch!!!!!!!!!------');
        return null;
    }
    global void execute(Database.BatchableContext BC, List<Contact> scope) {
           
        
        update toSave;

    }
    global void finish(Database.BatchableContext BC) {
        system.debug('------Finish Batch!!!!!!!!!------');
    }

}

And finally this is where everything is being called
 
public void afterUpdate(SObject oldSo, SObject so) {
          Account oldAcct = (Account) oldSo;
          Account acct = (Account) so;
          CCIProviderChangeAccountTracking.resaveContact(oldAcct,acct);

    }

 
Best Answer chosen by Jacob Elliott 1
Asif Ali MAsif Ali M
Hi Jacob,

You code is not bulkified. You need to change your trigger and then add a future method to update contacts.  If you have thousands of contacts linked to an account then you might need a batch job otherwise Future method would work fine in this case. please check the below code and adjust as per your need.
 
public void afterUpdate(Map<Id, Account> oldMap, Map<Id, Account> newMap) {

        Set<Id> accountSet = new Set<Id>();
        for (Account newAcct: newMap) {

            oldAcct = oldMap.get(newAcct.Id);

            if (oldAcct.Phone != newAcct.Phone ||
                    oldAcct.ShippingAddress != newAcct.ShippingAddress ||
                    oldAcct.V12C__PR_Practice_Tax_ID_Number__c != newAcct.V12C__PR_Practice_Tax_ID_Number__c ||
                    oldAcct.BillingAddress != newAcct.BillingAddress) {
                // Tracking all modified Account Ids
                accountSet.add(newAcct.Id);
            }
        }

        // Call future method to update contacts
        updateContacts(accountSet);
    }

    @future
    public static void updateContacts(Set<Id> accountSet){
        Contact[] contacts = [SELECT Id, Last_Changed_Date__c, AccountId FROM Contact WHERE AccountId IN :accountSet];

        for (Contact c: contacts){
            c.Last_Changed_Date__c = Datetime.now();
        }
        update contacts;
    }


 

All Answers

Asif Ali MAsif Ali M
Hi Jacob,

You code is not bulkified. You need to change your trigger and then add a future method to update contacts.  If you have thousands of contacts linked to an account then you might need a batch job otherwise Future method would work fine in this case. please check the below code and adjust as per your need.
 
public void afterUpdate(Map<Id, Account> oldMap, Map<Id, Account> newMap) {

        Set<Id> accountSet = new Set<Id>();
        for (Account newAcct: newMap) {

            oldAcct = oldMap.get(newAcct.Id);

            if (oldAcct.Phone != newAcct.Phone ||
                    oldAcct.ShippingAddress != newAcct.ShippingAddress ||
                    oldAcct.V12C__PR_Practice_Tax_ID_Number__c != newAcct.V12C__PR_Practice_Tax_ID_Number__c ||
                    oldAcct.BillingAddress != newAcct.BillingAddress) {
                // Tracking all modified Account Ids
                accountSet.add(newAcct.Id);
            }
        }

        // Call future method to update contacts
        updateContacts(accountSet);
    }

    @future
    public static void updateContacts(Set<Id> accountSet){
        Contact[] contacts = [SELECT Id, Last_Changed_Date__c, AccountId FROM Contact WHERE AccountId IN :accountSet];

        for (Contact c: contacts){
            c.Last_Changed_Date__c = Datetime.now();
        }
        update contacts;
    }


 
This was selected as the best answer
Jacob Elliott 1Jacob Elliott 1
Thanks so much Asif, that is very helpful!