Salesforce Marketing Blog

Salesforce Marketing Blog

Automated Multi-Wave Campaigns In Salesforce Marketing (Summer ’09 part 2)

John Kucera Jun 16, 2009

Have you ever wanted to automatically email prospects or customers based on rules you set up?  Ever wanted to email both leads and contacts without two separate templates and processes?  In Summer ’09, it’s as easy as creating a few campaign member workflow rules! 

    

In part 1, we walked through how triggers can be used to summarize campaign information.  In this example, we’ll show you how two workflow rules and a trigger can be used to auto-email a second reminder to non-respondents.  If any pieces seem tricky, your SFDC Administrator can help you set up these steps.


Continuing our example, Whitney has already created a parent campaign “2009-Q3-Webinar” and a child campaign “2009-Q3-Webinar-Email Reminder”.  Both of these campaigns include the Status "No Response-2nd Email". 


Whitney can work with her SFDC admin to create a workflow email alert to auto-email anyone that is added to the campaign.  However, we only want those that haven’t registered for the webinar to receive this second email. With Summer ’09, it’s now easy to auto-email campaign members with a workflow email alert, regardless of whether they are a lead or contact!  


We’ll walk through the key steps to set up this multi-campaign automation:
       

1) Create the time–dependent workflow rule, which delays the action by seven days
2) Create an after update trigger, which clones this campaign member to the “Reminder Email” campaign when the workflow fires
3) Create a workflow email alert, which auto-emails campaign members added to the “Reminder Email” campaign

Step 1: Workflow rule fires seven days after first email sent

Read More >



First, we want to create a rule that fires seven days after the person was first emailed, which in this case happened as soon as they were added to the “TechCo Webinar Email” campaign.  To create this workflow rule, navigate to Setup-->Create-->Workflow & Approvals-->Workflow Rules-->Workflow Rule-->New Rule, then select Campaign Member.


Campaign Member Email pic 1


Campaign Member Email pic 2


Next, select the Campaign Member object:


Campaign Member Email pic 3


After naming the rule, it’s time to set the rule criteria.  For this rule, we want the seven-day delay to start counting from when the record was created, so under Evaluation Criteria, we select “Only when a record is created.”  For the Rule Criteria, we select only members added to the campaign with a status of “Sent” and Campaign Name equal to “2009-Q3-Webinar”—the name of our campaign.


Campaign Member Email pic 4


Now that you have specified the conditions for the workflow rule to fire, specify what happens when it fires, and when.  In this example, we set the “when”, by clicking “Add Time Trigger, ” then entering seven days after the campaign member created date.


Campaign Member Email pic 5


Campaign Member Email pic 6


Next, set the action that will happen when the trigger fires.  In this case, we want it to update the Status to “No Response–2nd Email.” From “Add Workflow Action,” we select “New Field Update” and enter our criteria, then click “Save.”


Campaign Member Email pic 7


Campaign Member Email pic 8


Back on the Edit Rule page, click Done. Finally, activate the rule by clicking “Activate.”



Step 2: Trigger clones non-respondents to new campaign


Phew!  Now you might be wondering one of two questions:

1) Why can’t I just use an email alert on this campaign once the status changes?

2) What happens if someone already registered – won’t we be overwriting the status??


The reason we can’t simply create two workflow rules is that workflow cannot trigger workflow upon save.  We get around that problem by using this trigger to add the person to a new campaign.  The added bonus is that it’s a cleaner record of who was emailed and who wasn’t.


Our trigger resolves the second question as it checks if the status before the update was Sent, and if the new status is No Response–2nd Email.  If so, it puts this prospect into a second campaign called “2009-Q3-Webinar-Email Reminder.”   If not, it reverts the status, as we don’t want to overwrite RSVP-Yes statuses!


trigger CloneCampaignMember on CampaignMember (before update) {

  for (Integer i = 0; i < Trigger.new.size(); i++) {

    if (Trigger.old[i].Status == 'Sent' && Trigger.new[i].Status == 'No Response-2nd Email') {

                CampaignMember nurtureCM= Trigger.new[i].clone(false);

            try{  //try lets the trigger recover from a problem - such as if the campaign isn't found

                Campaign c = [SELECT Id FROM Campaign WHERE Name = '2009-Q3-Webinar-Email Reminder'];//pick the right campaign

                nurtureCM.CampaignID = c.Id; //assign this campaign to the new member

                nurtureCM.Status = 'No Response-2nd Email';  //set status for the new member

            }catch(Exception e){ // this 'catches' an error if the insert fails so that an ugly message doesn't occur

                System.debug('Campaign named 2009-Q3-Webinar-Email Reminder does not exist: ' + e); //this prints the error to the system log

            }//try

            try{

                insert nurtureCM; //Copy the newmember to the database         

            }catch(DMLException e){ // this 'catches' an error if the insert fails so that an ugly message doesn't occur

                System.debug('Campaign Member insert failed: ' + e); //this prints the error to the system log

            }//try

    }else {

        if (Trigger.new[i].Status == 'No Response-2nd Email'){

            try{  //try lets the trigger recover from a problem

                CampaignMember revertStatusCM= Trigger.new[i].clone(false);

                revertStatusCM.Status=Trigger.old[i].status;

                update revertStatusCM;

             }catch(DMLException e){ // this 'catches' an error if the insert fails so that an ugly message doesn't occur

                System.debug('Campaign Member status reversion failed:' + e); //this prints the error to the system log

            }//try

        }//if 2

    }//if 1       

  }//for

}//trigger

 


Campaign Member Email pic 10

Also, add a test case for the trigger.  Note this code below is not best practice as it doesn't have any system.asserts, which actually validate that the code is working as expected:

@isTest
private class Summer09AutomatedCampaignsTests{

    static testMethod void verifyCloneTrigger(){
        // Perform our data preparation.
        List<Lead> leads = new List<lead>{};

        for(Integer i = 0; i < 10; i++){
            Lead l = new Lead(LastName = 'Test Lead ' + i,Company='Company '+i);
            leads.add(l);
        }

        Campaign c= new Campaign(Name='2009-Q3-Event-Dreamforce 2009');
        insert c;

        Campaign c2= new Campaign(Name='2009-Q3-Event-Dreamforce 2009-Email Reminder');
        insert c2;

        campaign c3= [SELECT Id FROM Campaign WHERE Id= '70130000000AZVJ'];

        List<CampaignMember> campaignmembers = new List<CampaignMember>{};

        // Start the test, this changes governor limit context to
        // that of trigger rather than test.

        // Insert the Account records that cause the trigger to execute.
        insert leads;
        for(Integer i = 0; i < 10; i++){
            Id LeadIdi=leads[i].id;
            CampaignMember cm = new CampaignMember(CampaignID  = c3.Id,LeadId = LeadIdi,Status='Sent');
            campaignmembers.add(cm);
        }
        insert campaignmembers;
        test.startTest();

   


        for(Integer i = 0; i < 10; i++){
            campaignmembers[i].status='Sent';
        }
        update campaignmembers; 

        for(Integer i = 0; i < 10; i++){
            campaignmembers[i].status='No Response - 2nd Email';
        }
            update campaignmembers;           

        for(Integer i = 0; i < 10; i++){
            campaignmembers[i].status='RSVP-Yes';
        }
            update campaignmembers;   

       // Stop the test, this changes limit context back to test from trigger.
        test.stopTest();

        // Query the database for the newly inserted records.
        List<Lead> insertedLeads = [SELECT LastName, Company,LeadSource
                                          FROM Lead
                                          WHERE Id IN :leads];

        // Assert that the Description fields contains the proper value now.
    /*    for(l :insertedLeads){
          System.assertEquals(
            null,
            l.LeadSource);
        }
        */
    }
}

Step 3: Workflow auto-emails a reminder


Now that the members are set to be cloned, the last step is to create an email alert workflow rule.  Similar to the above, first we create the rule, which we want to fire when the member is added to the Email Reminder campaign with a status of “No Response-2nd Email”:


Campaign Member Email pic 11


Then we set the action – what we want to happen when this rule fires.  We want to send an email, so we pick “New Email Alert.”



We select our reminder email template, and under Recipient Type, select “Email Field.”  The field “Email Field:Email” is the Lead/Contact email address, which is the one we want.  Finally we can send one email to both leads and contacts at once!


Campaign Member Email pic 12


To finish, click “Done” then click “Activate” to activate the rule.


The Payoff


With this automated flow set up, leads or contacts added to the campaign, who still have the status of Sent after seven days, will be sent a reminder email.  This is one example of how you can use the building blocks of campaign member triggers and workflow to create multi-wave campaign.  You could copy and tweak any of the steps above to accomplish more complex flows, such a series of nurturing emails sent to new leads that haven’t been converted into opportunities.  Also, once the first flow is built, you can easily clone the pieces or change the filters for your next campaign!


Campaign Member Email pic 13


In the next post, you’ll see how to avoid lead dupes by leveraging Salesforce Sites as your campaign member registration pages!

 

9 Comments

cmsproducts

We are unable to add the value to the Campaign Member Status Picklist as described. It only allows Responded or Sent, but has not way of allowing additional values to be populated in the list.

John Kucera

To add or change Campaign Member Status values that are available, you go to the Campaign, and click on "Advanced Setup".

You can't add them directly from the setup tab as the Campaign determines which status values are available vs. one set for all campaigns. Send me a note if you have any problems:

jkucera at salesforce dot com

Gary Stockton

My apologies. I was not looking in the right place. This solved my problem.

cmsproducts

OK, I'm still struggling a little bit.

I implemented section1, however, It's not clear where in Salesforce I would go to implement section2. Would you be more specific for Step 2: Trigger clones non-respondents to new campaign.

John Kucera

To summarize our offline email thread:
Setup-->Customize-->Campaigns-->Campaign Members-->Triggers-->New is the route to create new triggers.

To have the "new" button for triggers appear, you must be:
1) have the "Author Apex" permission checked (all sys admins have this)
2) Be in a development, sandbox, or trial org - Apex cannot be written in Active orgs, it must be written in one of these other types of orgs & deployed to your Active production org

Tommy

John -
As part of the Apex Trigger Upload as a package instructions you left out that the system will NOT let you simply deploy your new Apex trigger from the sandbox to the production org. One has to also create an Apex Test Class and run it through testMethods before it'll allow it to be deployed. This Test Class creation is additional coding and software development, which a simple user shouldn't have to do or should have to figure out. It would be nice if you provide us all with the last piece of the puzzle to get this feature running, and give us the testMethod Apex Class code so we can deploy this feature properly. Thanks.

Aiden

Hi John,

Images in this posting and the previous post (Step 2) are missing. The instructions are less than clear without the screenshots.

Also, I have to agree with Tommy. A Test class is more than helpful in these examples - it's essential.

Thanks.

Aiden

John Kucera

Thanks for the comments - I've added a test case framework for triggers like this to the post. Note that you should add system.asserts to the test code above to check if your trigger works as you intend it to.

Aiden

Hi John,

Is there a way to mass email campaign members via sfdc's gui that would pass along the campaign member id and other fields from the campaign member object?

Thanks.

Aiden

Post a comment

If you have a TypeKey or TypePad account, please Sign In.