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
Darkangel8Darkangel8 

batch class to update lookup relationship fields with latest values get displayed

Want to create a batch class on contact object having lookup with account. When contact mobile number get updated then latest mobile umber will get reflected in account object. Please help to code this.
Best Answer chosen by Darkangel8
KdKomalKdKomal

Hi Dhanashree,

Please find the code. I hope it will get you job done(Just Change the fields as per your need)
A trigger which will be fired whenever a contact is updated/Inserted and update the account and a batch you can either schedule or trigger on an ad-hoc request.

Note :- I have implemented custom logic where the batch will pick only those records which are updated after the last batch run date/Time (after the first time). This will ensure that redundant records are not processed.
For this logic to work you need to create a custom setting which will have a field to store the last run batch date/time.


Please mark this as best answer if it solves your issue.

Trigger :- updateCallOnAcctTrigger

trigger updateCallOnAcctTrigger on Contact (after insert,after update) {
    set<id> actId = new set<id>();
    
    for(Contact cnt: Trigger.new){
        if(Trigger.isInsert && Trigger.isAfter) 
        	actId.add(cnt.AccountId);
        if(Trigger.isUpdate && Trigger.isAfter && 
           (Trigger.NewMap.get(Cnt.Id).phone != Trigger.OldMap.get(Cnt.Id).phone)){
        	actId.add(cnt.AccountId);       
           }     
    }
    
    if(actId.size() > 0){
        updateAccountNoBatch uptAcct = new updateAccountNoBatch(actId);
        List<Account> updateActList = uptAcct.processAccount();
        if(updateActList.size() > 0){
            update updateActList;
        }
    }

}

Batch Class :- updateAccountNoBatch
 
// Initialize a class with modifier as Global. For a batch class the class needs to implement the interface Database.Batchable
// and Schedulable for the class to be scheduled.
Global class updateAccountNoBatch Implements Schedulable, Database.Batchable<sObject>{
    // Declare a Map for AccountId --> Contact Number
    Map<String,String> mapAccountsToProcess;
	
    // Field To store Last Batch Run
    Datetime lastRun = null;
    
    // Constructor called in batch Mode
    global updateAccountNoBatch(){
    	Customization_Settings__c cs = Customization_Settings__c.getOrgDefaults();
        if(cs.Batch_Last_Run_Time__c != null)
			lastRun = cs.Batch_Last_Run_Time__c;
    }
    
    // Constructor called from trigger
    global updateAccountNoBatch(Set<Id> AcctIds){
    	mapAccountsToProcess = new Map <String,String>();
        for(Contact ct: [Select Id, AccountId,Phone from Contact where AccountId In : AcctIds order by lastmodifiedDate Desc Limit 1]){
        	if(ct.Phone != null) 
            	mapAccountsToProcess.put(ct.AccountId, ct.Phone);   
        }
    }
    // The Database.batchable has three methods to be implemented. This will be same for all the batch classes.
    // Start Method Query the data
    global Database.QueryLocator start(Database.BatchableContext BC){
        String query = '';
        if(lastRun != null)
        	query = 'Select Id, AccountId,Phone'+
            		'from Contact where AccountId IN (Select Id,AccountNumber from Account)'+
            		'and LastModified >: lastRun order by lastModifieddate Desc Limit 1';
        else
            query = 'Select Id, AccountId,Phone from Contact where AccountId IN (Select Id,AccountNumber from Account) order by lastModifieddate Desc Limit 1';
        return Database.getQueryLocator(query);
    }
    
    // Main Logic executes in This method
    global void execute(Database.BatchableContext BC, List<Contact> scope){
        mapAccountsToProcess = new Map <String,String>();
        for(Contact ct : scope){
            if(ct.Phone != null)
            	mapAccountsToProcess.put(ct.AccountId, ct.Phone);   
        }
        
        if(mapAccountsToProcess.size() > 0 ){
            List <Account> listAccountsToUpdate	= processAccount();			
			if (listAccountsToUpdate.size() > 0)
                update listAccountsToUpdate;
        }
    }
    
    // When all the records are performed upon, this function is called.
    global void finish(Database.BatchableContext BC){
        
    }
    
    
    // This is called when a class is scheduled.
    global void execute(SchedulableContext ctx) {
		updateAccountNoBatch s = new updateAccountNoBatch();        
        database.executebatch(s,200);         	
	}	
    
    // Real Processing is taking place here.
    public List<account> processAccount(){
        List<account> returnAct = new List<Account>();
        
        for(String str : mapAccountsToProcess.keySet()){
            Account actNew = new Account(Id = str);
            actNew.Phone = mapAccountsToProcess.get(Str);
            returnAct.add(actNew);
        }
        
        if(returnAct.size() > 0){
            return returnAct;
        }
        
        return null;
    }
}



 

All Answers

KdKomalKdKomal
Hi Dhanashree,

Have you formulated any approach or have an idea how to proceed with this ?
If not we can help but our suggestion would be to start yourself first and if you stumble upon any issue we will help you out.

Just a question, as multiple contacts might be associated with an account, how should one select which no to update on the Account from Contact.
Darkangel8Darkangel8
Hi Kd Komal,
Actually i am learning Apex so I don't have idea. I have created a custom object whose lookup with Account Object. I want to create a batch class where I can update mobile field value that will get reflected to Account object field. But condition is Only latest updated mobile number will get reflected on Account object.
KdKomalKdKomal

Hi Dhanashree,

Please find the code. I hope it will get you job done(Just Change the fields as per your need)
A trigger which will be fired whenever a contact is updated/Inserted and update the account and a batch you can either schedule or trigger on an ad-hoc request.

Note :- I have implemented custom logic where the batch will pick only those records which are updated after the last batch run date/Time (after the first time). This will ensure that redundant records are not processed.
For this logic to work you need to create a custom setting which will have a field to store the last run batch date/time.


Please mark this as best answer if it solves your issue.

Trigger :- updateCallOnAcctTrigger

trigger updateCallOnAcctTrigger on Contact (after insert,after update) {
    set<id> actId = new set<id>();
    
    for(Contact cnt: Trigger.new){
        if(Trigger.isInsert && Trigger.isAfter) 
        	actId.add(cnt.AccountId);
        if(Trigger.isUpdate && Trigger.isAfter && 
           (Trigger.NewMap.get(Cnt.Id).phone != Trigger.OldMap.get(Cnt.Id).phone)){
        	actId.add(cnt.AccountId);       
           }     
    }
    
    if(actId.size() > 0){
        updateAccountNoBatch uptAcct = new updateAccountNoBatch(actId);
        List<Account> updateActList = uptAcct.processAccount();
        if(updateActList.size() > 0){
            update updateActList;
        }
    }

}

Batch Class :- updateAccountNoBatch
 
// Initialize a class with modifier as Global. For a batch class the class needs to implement the interface Database.Batchable
// and Schedulable for the class to be scheduled.
Global class updateAccountNoBatch Implements Schedulable, Database.Batchable<sObject>{
    // Declare a Map for AccountId --> Contact Number
    Map<String,String> mapAccountsToProcess;
	
    // Field To store Last Batch Run
    Datetime lastRun = null;
    
    // Constructor called in batch Mode
    global updateAccountNoBatch(){
    	Customization_Settings__c cs = Customization_Settings__c.getOrgDefaults();
        if(cs.Batch_Last_Run_Time__c != null)
			lastRun = cs.Batch_Last_Run_Time__c;
    }
    
    // Constructor called from trigger
    global updateAccountNoBatch(Set<Id> AcctIds){
    	mapAccountsToProcess = new Map <String,String>();
        for(Contact ct: [Select Id, AccountId,Phone from Contact where AccountId In : AcctIds order by lastmodifiedDate Desc Limit 1]){
        	if(ct.Phone != null) 
            	mapAccountsToProcess.put(ct.AccountId, ct.Phone);   
        }
    }
    // The Database.batchable has three methods to be implemented. This will be same for all the batch classes.
    // Start Method Query the data
    global Database.QueryLocator start(Database.BatchableContext BC){
        String query = '';
        if(lastRun != null)
        	query = 'Select Id, AccountId,Phone'+
            		'from Contact where AccountId IN (Select Id,AccountNumber from Account)'+
            		'and LastModified >: lastRun order by lastModifieddate Desc Limit 1';
        else
            query = 'Select Id, AccountId,Phone from Contact where AccountId IN (Select Id,AccountNumber from Account) order by lastModifieddate Desc Limit 1';
        return Database.getQueryLocator(query);
    }
    
    // Main Logic executes in This method
    global void execute(Database.BatchableContext BC, List<Contact> scope){
        mapAccountsToProcess = new Map <String,String>();
        for(Contact ct : scope){
            if(ct.Phone != null)
            	mapAccountsToProcess.put(ct.AccountId, ct.Phone);   
        }
        
        if(mapAccountsToProcess.size() > 0 ){
            List <Account> listAccountsToUpdate	= processAccount();			
			if (listAccountsToUpdate.size() > 0)
                update listAccountsToUpdate;
        }
    }
    
    // When all the records are performed upon, this function is called.
    global void finish(Database.BatchableContext BC){
        
    }
    
    
    // This is called when a class is scheduled.
    global void execute(SchedulableContext ctx) {
		updateAccountNoBatch s = new updateAccountNoBatch();        
        database.executebatch(s,200);         	
	}	
    
    // Real Processing is taking place here.
    public List<account> processAccount(){
        List<account> returnAct = new List<Account>();
        
        for(String str : mapAccountsToProcess.keySet()){
            Account actNew = new Account(Id = str);
            actNew.Phone = mapAccountsToProcess.get(Str);
            returnAct.add(actNew);
        }
        
        if(returnAct.size() > 0){
            return returnAct;
        }
        
        return null;
    }
}



 
This was selected as the best answer
Darkangel8Darkangel8
Thank you so much kd Komal....Its working fine..
KdKomalKdKomal
Hi Dhanashree,

Glad i could help you out. Please mark this as a best answer so that others could get help as well from this . 
KdKomalKdKomal
Hi Dhanashree,

Please modify the below lines in your batch class (Sorry i made some mistakes) :-
Replace 
if(ct.Phone != null) with if(ct.Phone != null && (!mapAccountsToProcess.containsKey(ct.AccountId)))  

Remove all the "limit 1" clause from all the queries in the class and make sure the queries end with lastmodifieddate desc.

Hope this helps.
Darkangel8Darkangel8
Hi kd Komal.. Thank you for the changes.. i have created one custom field "Date_c" on account object and i want to run batch class to show the latest transaction date from child contact object. When I update the mobile field on contact object the last modified date will get reflected on Account objects "Date_c" field. is it possible ??
chandra prakash 58chandra prakash 58

Hi kd Komal,

Want to create a batch class on the custom object (enquiry__c) having lookup with the account. When enqiry__c  lookup field(City__c and State__c) get  matched with same field in Account object  then Account name  will get reflected in dealer(lookup field with account object) in  enquiry__c object. Please help to code this.