+ Start a Discussion
sam_Adminsam_Admin 

Schedule Apex is not updating records

Description: When the Primary campaign field from the Opp is Null then that field is updated by the most recent campaign from the primary contact role

Apex class: 

    global class UpdPrimaryCampaignOnOpty implements Database.Batchable<sObject>,Schedulable{
    
        global String Query;
        global UpdPrimaryCampaignOnOpty(){ 
            query = 'Select Id, contactId, opportunityId,isPrimary from OpportunityContactRole where (createddate = today or lastmodifieddate = today) and  opportunity.CampaignId = null';
        }
        
        global void execute(SchedulableContext SC){
            UpdPrimaryCampaignOnOpty upc =  new UpdPrimaryCampaignOnOpty();
            ID batchprocessid = Database.executeBatch(upc);
        }
        
         global DataBase.QueryLocator start(Database.BatchableContext BC){ 
            return Database.getQueryLocator(query);
        }
        global void execute(Database.BatchableContext BC, List<sObject> scope){
            System.debug('scope size = '+scope.size());
            set<Id> conIds = new Set<Id>();
            Set<Id> optyIds = new Set<Id>();
            Map<Id,Id> optyConMap = new Map<Id,Id>();
            for(sObject s : scope){
                OpportunityContactRole ocr = (OpportunityContactRole)s;
                if(ocr.isPrimary){
                    conIds.add(ocr.contactId);
                    optyIds.add(ocr.opportunityId);
                    optyConMap.put(ocr.OpportunityId, ocr.ContactId);
                }
            }
            System.debug('conIds size = '+conIds.size());
            System.debug('optyIds size = '+optyIds.size());
            System.debug('optyConMap size = '+optyConMap.size());
            
            List<CampaignMember> cmList = [Select id, CampaignId, ContactId, Campaign.createddate from CampaignMember where ContactId IN :conIds];
            System.debug('cmList size = '+cmList.size());
            Map<Id,CampaignMember> contactCampaignMap = new Map<Id,CampaignMember>();
            for(CampaignMember cm : cmList){
                
                if(contactCampaignMap.containsKey(cm.contactId) && cm.Campaign.createddate > contactCampaignMap.get(cm.contactId).Campaign.CreatedDate ){
                    contactCampaignMap.put(cm.contactId, cm);
                }
                else{
                    contactCampaignMap.put(cm.contactId, cm);
                }           
            }
            
            List<Opportunity> optyList = [Select Id, CampaignId from Opportunity where Id IN :optyIds];
            
            for(Opportunity opty : optyList){
                if(optyConMap.containsKey(opty.id) && contactCampaignMap.containsKey(optyConMap.get(opty.Id))){
                    opty.CampaignId = contactCampaignMap.get(optyConMap.get(opty.Id)).CampaignId;
                }        
            }
            
            update optyList;
            
        }
        
        global void finish(Database.BatchableContext BC){
           
       }
            
    }
Best Answer chosen by sam_Admin
jigarshahjigarshah
Sam,

There are a couple of things that you should check for.
  • Check the Debug Logs when this batch job executes and look for Soql queries that return 0 records. This should be your starting point for investigation.
  • Also, the Soql query below is comparing CreatedDate and LastModifiedDate which are DateTime fields with a value TODAY which is only the date component. Check if this query returns the expected result set.
Select Id, contactId, opportunityId,isPrimary from OpportunityContactRole where (createddate = today or lastmodifieddate = today) and  opportunity.CampaignId = null
  • Your code for populating the contactCampaignMap which is checking for a condition does not make sense because it is doing the same thing in if and else and hence should be rewritten as below. The default behaviour oa a Map is in case a matching key value already exists in a map, the existing value against that key, is updated with the new value. Additionally using a soql for loop saves you on using an additional variable.
for(CampaignMember cm : [Select id, CampaignId, ContactId, Campaign.createddate 
						 From CampaignMember 
						 Where ContactId IN :conIds]){

	contactCampaignMap.put(cm.contactId, cm);
}
  • Also check if the following query, gives you the appropriate result set by printing its result to the Debug Logs.
Select Id, CampaignId from Opportunity where Id IN :optyIds

All Answers

Raj VakatiRaj Vakati
Here is the cod e
global class UpdPrimaryCampaignOnOpty implements Database.Batchable<sObject>,Schedulable{
    
    global String Query;
    global UpdPrimaryCampaignOnOpty(){ 
        query = 'Select Id, contactId, opportunityId,isPrimary from OpportunityContactRole where (createddate = today or lastmodifieddate = today) and  opportunity.CampaignId = null';
    }
    
    global void execute(SchedulableContext SC){
        UpdPrimaryCampaignOnOpty upc =  new UpdPrimaryCampaignOnOpty();
        ID batchprocessid = Database.executeBatch(upc);
    }
    
    global DataBase.QueryLocator start(Database.BatchableContext BC){ 
        return Database.getQueryLocator(query);
    }
    global void execute(Database.BatchableContext BC, List<sObject> scope){
       set<Id> conIds = new Set<Id>();
        Set<Id> optyIds = new Set<Id>();
        Map<Id,Id> optyConMap = new Map<Id,Id>();
        for(sObject s : scope){
            OpportunityContactRole ocr = (OpportunityContactRole)s;
            if(ocr.isPrimary){
                conIds.add(ocr.contactId);
                optyIds.add(ocr.opportunityId);
                optyConMap.put(ocr.OpportunityId, ocr.ContactId);
            }
        }
        
        
          
        List<CampaignMember> cmList = [Select id, CampaignId, ContactId, Campaign.createddate from CampaignMember where ContactId IN :conIds Order by Campaign.createddate ];
        System.debug('cmList size = '+cmList.size());
        Map<Id,CampaignMember> contactCampaignMap = new Map<Id,CampaignMember>();
        for(CampaignMember cm : cmList){
            if(!contactCampaignMap.containsKey(cm.contactId)){
                contactCampaignMap.put(cm.contactId, cm);
            }
                      
        }
       List<Opportunity> optyList = [Select Id, CampaignId from Opportunity where Id IN :optyIds];
        List<Opportunity> opp = new List<Opportunity>();
        for(Opportunity opty : optyList){
            if(optyConMap.containsKey(opty.id) && contactCampaignMap.containsKey(optyConMap.get(opty.Id))){
                opty.CampaignId = contactCampaignMap.get(optyConMap.get(opty.Id)).CampaignId;
                opp.add(opty);
            }        
        }
        
        update opp;
        
        
    }
    
    global void finish(Database.BatchableContext BC){
        
    }
    
}

 
jigarshahjigarshah
Sam,

There are a couple of things that you should check for.
  • Check the Debug Logs when this batch job executes and look for Soql queries that return 0 records. This should be your starting point for investigation.
  • Also, the Soql query below is comparing CreatedDate and LastModifiedDate which are DateTime fields with a value TODAY which is only the date component. Check if this query returns the expected result set.
Select Id, contactId, opportunityId,isPrimary from OpportunityContactRole where (createddate = today or lastmodifieddate = today) and  opportunity.CampaignId = null
  • Your code for populating the contactCampaignMap which is checking for a condition does not make sense because it is doing the same thing in if and else and hence should be rewritten as below. The default behaviour oa a Map is in case a matching key value already exists in a map, the existing value against that key, is updated with the new value. Additionally using a soql for loop saves you on using an additional variable.
for(CampaignMember cm : [Select id, CampaignId, ContactId, Campaign.createddate 
						 From CampaignMember 
						 Where ContactId IN :conIds]){

	contactCampaignMap.put(cm.contactId, cm);
}
  • Also check if the following query, gives you the appropriate result set by printing its result to the Debug Logs.
Select Id, CampaignId from Opportunity where Id IN :optyIds
This was selected as the best answer
sam_Adminsam_Admin
Raj,
  I tried your code and executed batch apex through console but records didn't get updated  
jigarshahjigarshah
Sam, 

I just want to ensure that you are executing this class in the expected way. If you want to execute the above stated Batch Job code you will need to use the following code to invoke it. You can then check the status of the executing job under Setup > Scheduled Apex Jobs within your Salesforce instance.
Id bactchJobId = database.executeBatch(new UpdPrimaryCampaignOnOpty(), 200);

If you are using Database.schedule() to execute the above code from your Developer Console, you need to be aware that Salesforce does not necessarily execute the job at the specified time but just queues it for processing. As and when resources become available, the job is considered for processing. Hence, the Scheduled Apex Jobs console comes is handy to monitor the status.
sam_Adminsam_Admin
Shah,  
     I removed (createddate =today or lastmodifieddate = today) from below query and it worked, not sure why i had that at in my code but thanks for the help
Select Id, contactId, opportunityId,isPrimary from OpportunityContactRole where (createddate =today or lastmodifieddate = today) and  opportunity.CampaignId = null
jigarshahjigarshah
Sam, glad was able to help you. Cheers!