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
devrequirementdevrequirement 

Converting Trigger to Batch Apex: Help needed to make the code work

If possible, could anyone please direct me towards the right direction? The second code (Batch Apex) is just not compiling. Currently the error is,

Error: Compile Error: Invalid type: updateContactOnEmailOptOutChangeScheduler at line 63 column 73

 

 

 

 

Original Trigger Code

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

trigger updateContactOnEmailOptOutChange on Contact (after update) {                                                                                          
     
    //Initialize lists and maps

    List<Contact> duplicateContacts = new List<Contact>();
    Map<String, Contact> contactEmailMap = new Map<String, Contact>();
    Map<Id, Contact> contactIdMap = new Map<Id, Contact>();

    //Build a map with contacts to update. Only select the ones that have a different "Email Opt Out" value from the contact being updated.

    for (Integer i = 0; i < Trigger.new.size(); i++) {
        if (Trigger.old[i].HasOptedOutOfEmail != Trigger.new[i].HasOptedOutOfEmail) {
            contactEmailMap.put(Trigger.old[i].email, Trigger.new[i]);
            contactIdMap.put(Trigger.old[i].id, Trigger.new[i]);        
        }
    }    
    
    //Only go through this process if "Email Opt Out" (HasOptedOutofEmail) was updated.
    
    If (contactIdMap.size()>0) {
    
        //Query the database and look for all contacts with a duplicate email address (same email as the contact currently being updated).

        for (Contact dupContact : [SELECT Id, Name, Email, HasOptedOutOfEmail
                                   FROM Contact
                                   WHERE Email IN : contactEmailMap.KeySet()
                                   AND Id NOT IN : contactIdMap.KeySet()]) {
            Contact contact = contactEmailMap.get(dupContact.Email);
            If (dupContact.HasOptedOutOfEmail <> contact.HasOptedOutOfEmail) { 
                dupContact.HasOptedOutOfEmail = contact.HasOptedOutOfEmail;   
                duplicateContacts.add(dupContact);
            }
        }    
    
        //If any duplicate contacts were found, update all duplicate contacts with the new HasOptedOutOfEmail value.

       If (duplicateContacts.size()>0) update duplicateContacts;
   }
}

 

Batch Apex

 

global class updateContactOnEmailOptOutChange implements Database.Batchable<sObject>
{
    global string query;
   
    global updateContactOnEmailOptOutChange()
    {
    query = 'SELECT id,Name, Email, HasOptedOutofEmail from Contact where HasOptedOutofEmail=true';
    }
   
    global Database.QueryLocator start(Database.BatchableContext BC)
    {
    return Database.getQueryLocator(query);
    }

   
    global void execute(Database.BatchableContext BC, List <sObject> duplicateContacts)
    {
    Map<String, Contact> contactEmailMap = new Map<String, Contact>();
    Map <Id, Contact> contactIdMap = new Map<Id, Contact>();
   
    // Build a map with contacts to update. Only select the ones that have a different "Email Opt Out" value from the contact being updated.
    if(trigger.isUpdate){

    for(Integer i=0; i<Trigger.new.size();i++)
      {
      if(Trigger.old[i].HasOptedOutOfEmail != Trigger.new[i].HasOptedOutOfEmail)
          {
          contactEmailMap.put(Trigger.old[i].email, Trigger.new[i]);
          contactIdMap.put(Trigger.old[i].id, Trigger.new[i]);
          }
      }   
   
    if(contactidMap.size()>0)
    {
    //Query the database and look for all contacts with a duplicate email address(same email as the contact currently being updated)
    for (Contact dupContact: [SELECT Id, Name, Email, HasOptedOutofEmail
                              FROM Contact
                              WHERE Email IN: contactEmailMap.KeySet()
                              AND Id NOT IN: contactIdMap.KeySet()])
                                  {
                                  Contact contact=contactEmailMap.get(dupContact.Email);
                                  If(dupContact.HasOptedOutOfEmail <> contact.HasOptedOutOfEmail)
                                  {
                                  dupContact.HasOptedOutOfEmail = contact.HasOptedOutOfEmail;
                                  duplicateContacts.add(dupContact);
                                  }
                                  }
    // if any duplicate contacts were found, update all duplicate contacts with the new HasOptedOutOFEmail value.
   
    If(duplicateContacts.size<>0) update duplicateContacts;
    }
    }
    }
   
    //The batch process has completed successfully. Schedule next batch.
   
    global void finish(Database.BatchableContext BC){
    // //Build the system time of now + 300 seconds to schedule the batch apex.
    Datetime sysTime = System.now();
    sysTime = sysTime.addSeconds(300);
    String chron_exp=''+sysTime.second()+''+sysTime.minute()+''+sysTime.hour()+''+sysTime.day()+''+sysTime.month()+'?'+sysTime.year();
    system.debug(chron_exp);
    updateContactOnEmailOptOutChangeScheduler scheduleFieldUpdate = new updateContactOnEmailOptOutChangeScheduler();
    //Schedule the next job, and give it the system time so name is unique
    System.schedule('New Email Update Job'+sysTime.getTime(),chron_exp,scheduleFieldUpdate);
    }
    }

 

bob_buzzardbob_buzzard

This is complaining that the compiler can't find a class named updateContactOnEmailOptOutChangeScheduler.  You haven't posted this class - does it exist?

devrequirementdevrequirement

Thanks for the reply Bob!

 

I was thinking about the other way in which this requirement could be solved.

 

Here's the trigger that would call batch apex when the HasOptedOutOfEmail has changed:

 

/**The trigger code where we check if the field HasOptedOutOfEmail has changed and if changed, implement batch apex to update all duplicate
contacts with the new HasOptedOutOfEmail value**/

trigger updateonEmailOptOutChange on Contact (after update) {
Map<string, Contact> contactEmailMap = new Map<String, Contact>();
Map<Id, Contact> contactIdMap = new Map<Id, Contact>();

// Build a Map with contacts to update. Only select the ones that have a different "Email Opt Out' value from the contact being updated.
for (integer i=0; i< Trigger.new.size();i++){
    if(Trigger.old[i].HasOptedOutOfEmail != Trigger.new[i].HasOptedOutOfEmail){
    contactEmailMap.put(Trigger.old[i].email, Trigger.new[i]);
    contactIdMap.put(Trigger.old.id, Trigger.new[i]);
    }
}

// Call the batch apex only if "Email Opt Out" (HasOptedOutOfEmail) field was updated.
if(contactIdMap.size() > 0){
   Database.executeBatch(new HasOptedOutOfEmailBatch(contactIdMap));
   }
}

 

And here's the batch apex that will do the job:

 

global class HasOptedOutOfEmailBatch implements Database.Batchable<sObject>
{
// map of contactid and contact
    Map<Id, Contact> ContactIdMap = new Map<Id, Contact> ();
    Map<String,Contact> contactEmailMap = new Map<String,Contact>();
    List<Contact> duplicateContacts = new List<Contact>();
   
    global HasOptedOutOfEmailBatch(Map<Id,Contact> ContactId, Map<String,Contact> contactEmail)
    {
    ContactIdMap = ContactId;
    ContactEmailMap =contactEmail;
    }
   
    global Database.QueryLocator start(Database.BatchableContext BC)
    {
    return Database.getQueryLocator([SELECT Id, Name, Email, HasOptedOutOfEmail FROM Contact
                                    WHERE Email IN: contactEmailMap.KeySet()
                                    AND Id NOT IN: contactIdMap.KeySet()]);
    }
   
    global void execute(Database.BatchableContext BC, List <Contact> dupContact)
    {
    for(integer i=0; i<dupContact.size();i++){
    Contact contact = contactEmailMap.get(dupContact.Email);  >>>>>> Line 24
    If(dupContact.HasOptedOutOfEmail <> contact.HasOptedOutOfEmail){
    dupContact.HasOptedOutOfEmail = contact.HasOptedOutOFEmail;
    duplicateContacts.add(dupContact);
    }
    }
    update duplicateContacts;
    }
   
    global void finish(Database.BatchableContext BC)
    {
    //Send an email to the User after the batch completes
    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    String[] toAddresses = new String[] {'craigls@gmail.com'};
    mail.setToAddresses(toAddresses);
    mail.setSubject('Apex Batch Job of Email Opt Out field is done');
    mail.setPlainTextBody('The Batch Apex Job processed');
    Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail});
    }
}

 

Before I save the trigger, I must have the batch class ready. However, now I am getting the error 'Initial term of field expression must be a concrete SObject: LIST<Contact> at line 24 column 43'.

 

 

Again, the requirement is whenever a contact update happens, in the field of "Email Opt Out" (which is a checkbox), the duplicate contacts that have different 'Email Opt Out' value are also updated.

 

Thanks!!

 

bob_buzzardbob_buzzard

In this line:

 

contactIdMap.put(Trigger.old.id, Trigger.new[i]);

 

Trigger.old.id should be Trigger.old[i].id, I reckon.

 

Also, be wary when firing scheduled/batch apex from triggers.  If a lot of users change the contact information in a short timeframe, you can end up breaching the limits which results in jobs being removed from the queue.

devrequirementdevrequirement

Now, I am having another issue. I just wrote a test class to see to see if the duplicate contacts are actually being updated or not. But it's showing an error.

 

 

Trigger:

 

/**The trigger code where we check if the field HasOptedOutOfEmail has changed and if changed, implement batch apex to update all duplicate
contacts with the new HasOptedOutOfEmail value**/

trigger updateonEmailOptOutChange on Contact (after update) {
Map<string, Contact> contactEmail = new Map<String, Contact>();
Map<Id, Contact> contactId = new Map<Id, Contact>();

// Build a Map with contacts to update. Only select the ones that have a different "Email Opt Out' value from the contact being updated.
for (integer i=0; i< Trigger.new.size();i++){
    if(Trigger.old[i].HasOptedOutOfEmail != Trigger.new[i].HasOptedOutOfEmail){
    contactEmail.put(Trigger.old[i].email, Trigger.new[i]);
    contactId.put(Trigger.old[i].id, Trigger.new[i]);
    }
}

// Call the batch apex only if "Email Opt Out" (HasOptedOutOfEmail) field was updated.
if(contactId.size() > 0){
   Database.executeBatch(new HasOptedOutOfEmailBatch(contactId,contactEmail));
   }
}

 

Batch Apex

 

global class HasOptedOutOfEmailBatch implements Database.Batchable<sObject>
{
// map of contactid and contact
    Map<Id, Contact> ContactIdMap = new Map<Id, Contact> ();
    Map<String,Contact> contactEmailMap = new Map<String,Contact>();
    List<Contact> duplicateContacts = new List<Contact>();
   
    global HasOptedOutOfEmailBatch(Map<Id,Contact> ContactId, Map<String,Contact> contactEmail)
    {
    ContactIdMap = ContactId;
    ContactEmailMap =contactEmail;
    }
   
    global Database.QueryLocator start(Database.BatchableContext BC)
    {
    return Database.getQueryLocator([SELECT Id, Name, Email, HasOptedOutOfEmail FROM Contact
                                    WHERE Email IN: contactEmailMap.KeySet()
                                    AND Id NOT IN: contactIdMap.KeySet()]);
    }
   
    global void execute(Database.BatchableContext BC, List <Contact> duplicateContacts)
    {
    // for(integer i=0; i<dupContact.size();i++){
    for (Contact dupContact:[SELECT Id, Name, Email, HasOptedOutOfEmail FROM Contact
                                    WHERE Email IN: contactEmailMap.KeySet()
                                    AND Id NOT IN: contactIdMap.KeySet()]){

    Contact contact = contactEmailMap.get(dupContact.Email);
    If(dupContact.HasOptedOutOfEmail <> contact.HasOptedOutOfEmail){
    dupContact.HasOptedOutOfEmail = contact.HasOptedOutOFEmail;
    duplicateContacts.add(dupContact);
    }
    }
    update duplicateContacts;
    }
   
    global void finish(Database.BatchableContext BC)
    {
    //Send an email to the User after the batch completes
    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    String[] toAddresses = new String[] {'craigls@gmail.com'};
    mail.setToAddresses(toAddresses);
    mail.setSubject('Apex Batch Job of Email Opt Out field is done');
    mail.setPlainTextBody('The Batch Apex Job processed');
    Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail});
    }
}

 

TEST CLASS

 

@isTest(SeeAllData =true)

private class updateonEmailOptOutChangeTest
{  
    static testMethod void updateonEmailOptOutChangeTest() 
        {
          Contact ConObj1=new Contact();
          ConObj1.Email='TestUser1@gmail.com';
          //conObj1.Id='0038000001ANnQrISH';          
          ConObj1.HasOptedOutOfEmail = False;
          ConObj1.LastName = 'Mathema' ;
          
          insert conObj1;
          
          Contact ConObj2 = new Contact();
          ConObj2.Email = 'TestUser1@gmail.com';
          //conObj1.Id='D00000000000000';
          ConObj2.HasOptedOutOfEmail =False;
          ConObj2.LastName='Mathema' ;
          
          insert ConObj2;
          
          conObj1.HasOptedOutOfEmail= True;
          update ConObj1;

          System.assert(ConObj1.HasOptedOutOfEmail == ConObj2.HasOptedOutOfEmail);
    }
}