Thursday 29 April 2021

[SFMC Email Campaign]: Emails Send to Data Extension Not Delivered to all Subscribers

INTRODUCTION

While working on one of the requirements for a transportation sector project for a client based out of Melbourne, Australia, there was a requirement to run an Email Campaign using an automation studio with data source as DataExtension in Salesforce Marketing Cloud.


We've used SQL Query activity in automation studio to get filtered leads from DataExtension based on filter criteria (e.g. Date Range, Status etc.) and add it to another filtered DataExtension. Then the filtered DataExtension is used as a data source in Email Activity to run campaign. 


Challenge

The campaign ran successfully at the specified time, but looking at the SFMC Email Tracking, we have found that none of the emails was sent out even though all the records were having proper emails. 


Digging out in detail, we have found that due to Email field of data extension has a text data type.

 


APPROACH\RESOLUTION

First, we have removed the email field having text data type from filtered data extension. Then we have added new email field with Email data type and ran the campaign. This time, we were able to see emails sent out in SFMC email tracking.


Marketing Cloud uses the Subscriber Relationship to see a subscriber exists in All Subscribers and uses the email address field from the All Subscribers List as email address where email to be sent out.


If a subscriber does not exist in the All Subscribers List, OR there are several fields with an EmailAddress data type in the DataExtension, OR there isn't any field with an EmailAddress data type in the Data Extension, then system does not know which email address to use and will not send to those subscribers.


To ensure proper email delivery, we have to modify the DataExtension so that there should be only one field with an EmailAddress data type, and it should have the desired email address to be sent. 

If you have any questions you can reach out our Salesforce Consulting team here.

Thursday 22 April 2021

Introduction to Workflow Rule in Salesforce

INTRODUCTION

The workflow rule in Salesforce is one kind of process automation that basically contains the business logic that automates certain actions based on particular criteria.

What are the benefits of using Workflow Rules?

In a workflow rule, you get one set of criteria with multiple conditions and then a list of actions to run once the criteria is met. Being able to ‘chain’ multiple sets of criteria provides for if-then-else logic to easily build complex business processes. With traditional workflow rules, you would need several different rules to catch the different scenarios in your requirements. This can make complex business processes difficult to maintain.

There are endless ways you can use workflows, but of course, it is easier to illustrate what is a workflow in Salesforce with some examples. Mentioning the specific things that can be performed, you may automate the following 4 workflow actions in Salesforce:
  • creating tasks
  • Updating fields
  • Sending email alerts
  • Sending outbound messages

How to create workflow rules?
  • From Setup, enter the workflow rules in the Quick Find box, then select Workflow Rules.
  • Click New Rule.
  • Choose the object to which you want this workflow rule to apply.
  • Click Next.
  • Give the rule a name and description.
  • Set the evaluation criteria.(As per your requirement)
    • Created – Evaluate the rule criteria each time a record is created. If the rule criteria is met, run the rule. Ignore all updates to existing records.
    • Created, and every time it’s edited –Evaluate the rule criteria each time a record is created or updated. If the rule criteria is met, run the rule.
    • Created, and any time it’s edited to subsequently meet criteria - (Default) Evaluate the rule criteria each time a record is created or updated.


  • Enter your rule criteria.
  • Click Save & Next.

What are the Workflow Actions?

Email Alert: Email alerts are emails generated by an automated process and sent to designated recipients. These actions consist of the standard text and list of recipients for an email.

Field Update: A common use case of workflow rules is the function to overwrite field values with new data. This is, perhaps, the most frequently used action but please note that it won’t work in the cross-object case.

Outbound Message: An outbound message sends information to a designated endpoint, like an external service. You configure outbound messages from Setup. Provide the external endpoint and create a listener for the messages using the SOAP API. You can associate outbound messages with workflow rules, approval processes, or entitlement processes.

Task Creation: Task actions determine the details of an assignment given to a specified user by an automated process. You can associate task actions with workflow rules, approval processes, or entitlement processes.

How to Add Time-Dependent Action in Workflow Rule?
  • Open a workflow rule.
  • In the Time-Dependent Workflow Actions section, click Add Time Trigger.
  • Specify the number of days or hours before or after a date that’s relevant to the record, such as the date the record was created. Refer below image for better understanding.


  • Save your time trigger.
  • In the section for the time trigger you created, click Add Workflow Action.
  • Select one of the options to create an action or select an existing one.
  • Click Done.
If the workflow rule is still active and valid when this time occurs, the time trigger fires the workflow action.

Limitations of Workflow Rules:
  • You can’t add a time trigger if:
    • The evaluation criteria are set to evaluate the rule when a record is: Created, and any time it’s edited to subsequently, meet criteria.
    • The rule is activated.
    • The rule is deactivated but has pending actions in the workflow queue.
  • Please refer to the link for limitations.

SUMMARY

As illustrated, Salesforce Workflow is a much better and more efficient tool that let Salesforce admin creates business solutions with no code and with few clicks. So, we can easily perform some actions like email send, field update, etc.
        
    If you have any questions you can reach out to our Salesforce Consulting team here.

    Thursday 15 April 2021

    Mass\Bulk Operations in Salesforce using Batch Apex

    INTRODUCTION/SCENARIO

    There was a requirement for Mass/bulk Update of  more than thousands of records on the particular object based on certain action on object for one of our clients - consulting firm based out of Atlanta, GA, USA.

    We, all are familiar with updating records using Apex class but updating more than a thousand records or fire DML on thousands of rows on particular objects is very complex in Salesforce and it does not allow you to operate on more than a certain number of records which satisfy the Governor limits.

    But for medium to large enterprises, it is essential to manage thousands of records every day. Adding/Updating/Deleting them when needed. Salesforce has come up with a powerful concept called Batch Apex. It allows you to handle a thousand number of records and manipulates them by using a specific syntax.

    APPROACH
    For updating the thousand of records we need to develop the Batch Apex class which can mass update the records on the particular object. As Batch Apex operates over small batches of records, covering your entire record set and breaking the processing down to manageable chunks of data.

    PROCESS
    We have developed a custom global apex class that extends Database.Batchable interface because Salesforce compiler will come to know, this class incorporates batch jobs. Below is a sample class that is designed to update all the records of Account object (Let's say your organization contains more than 50 thousand records and you want to mass update all of them). 

    batch class
    After using the code, we have to go to Developer Console and click on Debug and then Open Execute Anonymous Window & Enter the following code in the box and click on Execute to see the result.
    summary
    Using Batch Apex, we can perform Adding/Updating/Deleting of thousands number of records for a particular object. As Batch Apex is asynchronous execution of Apex code, specially designed for processing a large number of records and has greater flexibility in governor limits than the synchronous code.

    If you have any questions you can reach out our Salesforce Consulting team here.

    Thursday 8 April 2021

    Salesforce Lightning TreeGrid with pagination

    INTRODUCTION/SCENARIO

    While working on one of the user stories of Pharma sector for a client based on Chicago, USA; there was a requirement to display Contact records under their parent account record. Also, pagination was needed with a picklist to choose number of records to be displayed on a page.

    CHALLENGE

    Lightning:treeGrid is useful component for displaying structured data such as hierarchy or forecasting data while dealing with the same object. But there is no functionality available to display records of 2 different objects (In our case, Account and Contact) in the same table while using Lightning:treeGrid.

    APPROACH / SOLUTION

    To overcome this limitation, I've implemented custom Javascript in the lightning component containing the mechanism to have customized list of Accounts and Contacts with pagination.

    Note: This component requires API version 42.0 and later.

    treeGridController is a controller class of the lightning components treeGrid utilized to fetch Account object data which has having child Contact records.

    treeGridController.apxc
    public class treeGridController {
        
        @AuraEnabled
        public static List <Account> getAccountList() {
            return [Select Id, Name,
                        (SELECT Name, Phone, Email FROM Contacts) 
                        From Account
                        Where Id IN (Select AccountId From Contact)
                        ORDER BY Name ASC];
        }
    }
    

    Below are Component & JavaScript files for the reference which i've used to meet the requirement..

    treeGrid.cmp
    <aura:component controller="treeGridController" implements="flexipage:availableForAllPageTypes" >
        <aura:attribute name="resultData" type="Object" access="private"/>
        <aura:attribute name="gridColumns" type="List" />
        <aura:attribute name="gridData" type="Object" />
        <aura:attribute name="gridExpandedRows" type="Object" />
        <aura:attribute name="PageNumber" type="Integer" />
        <aura:attribute name="TotalPages" type="Integer"/>
        <aura:attribute name="currentPage" type="Integer" default="0" />
        <aura:attribute name="limit" type="Integer" default="5" />
        <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
        <div class="slds-page-header" role="banner">
            <ui:inputSelect aura:id="pageSize" label="Display Records Per Page: " change="{!c.onSelectChange}">
                <ui:inputSelectOption label="5" text="5" value="true"/>
                <ui:inputSelectOption label="10" text="10"/>
                <ui:inputSelectOption label="50" text="50"/>
            </ui:inputSelect>
        </div>
        <lightning:treeGrid aura:id="accTree"
                            columns="{!v.gridColumns}"
                            data="{!v.gridData}"
                            expandedRows="{!v.gridExpandedRows}"
                            keyField="Id"
                            hideCheckboxColumn = "true"
                            />
        <div class="slds-clearfix">
            <div class="slds-page-header" role="banner">
                <div class="slds-float_right">            
                    <lightning:button disabled="{!v.PageNumber == 1}" variant="brand" aura:id="prevPage" label="Prev" onclick="{!c.handlePrev}" />            
                    <lightning:button disabled="{!v.PageNumber == v.TotalPages}" aura:id="nextPage" variant="brand" label="Next" onclick="{!c.handleNext}"/>
                </div>
                <p class="slds-page-header__title">Page {!v.PageNumber} of {!v.TotalPages}</p>
            </div>
        </div>
    </aura:component>
    

    treeGridController.js
    ({
        doInit : function(component, event, helper) {
            var columns = [
                {
                    type: 'url',
                    fieldName: 'AccountURL',
                    label: 'Account Name',
                    typeAttributes: {
                        label: { fieldName: 'accountName' }
                    }
                },
                {
                    type: 'text',
                    fieldName: 'Name',
                    label: 'Contact Name'
                },
                {
                    type: 'phone',
                    fieldName: 'Phone',
                    label: 'Phone Number'
                },
                {
                    type: 'email',
                    fieldName: 'Email',
                    label: 'Email'
                }
            ];
            component.set('v.gridColumns', columns);
            var action = component.get("c.getAccountList");
            action.setCallback(this, function(response){
                var state = response.getState();
                if (state === "SUCCESS" ) {
                    var resultData = response.getReturnValue();
                    component.set('v.resultData', resultData);
                    helper.bindTableData(component, event);
                }
            });
            $A.enqueueAction(action);
        },
        handleNext : function(component, event, helper){
            component.set('v.currentPage',component.get('v.currentPage')+1);
            helper.buildTable(component, event);
        },
        handlePrev : function(component, event, helper){
            component.set('v.currentPage',component.get('v.currentPage')-1);
            helper.buildTable(component, event);
        },
        onSelectChange : function(component, event, helper){
            component.set('v.currentPage',0);
            var pageSize = component.find('pageSize').get('v.value');
            component.set('v.limit',pageSize);
            helper.buildTable(component, event);
        }
    })
    

    treeGridHelper.js
    ({
        bindTableData : function(component, event) {
            var resultData = component.get('v.resultData');
            for (var i=0; i<resultData.length; i++ ) {
                resultData[i].accountName = resultData[i]['Name'];
                delete resultData[i]['Name'];
                resultData[i]._children = resultData[i]['Contacts'];
                delete resultData[i].Contacts;
                resultData[i].AccountURL = '/'+resultData[i].Id;                
            }
            component.set('v.resultData',resultData);
            this.buildTable(component, event);
        },
        buildTable : function(component, event){
            var resultData = component.get('v.resultData');
            var limit = component.get('v.limit');
            var currentPage = component.get('v.currentPage');
            var totalPage = Math.ceil(resultData.length / limit);
            var startIndex = currentPage * limit;
            var row = [];
            var expandedRows = [];        
            for (var i = startIndex; i < parseInt(startIndex)+parseInt(limit); i++) {
                if(resultData[i]){
                    expandedRows.push(resultData[i].Id);
                    row.push(resultData[i]);
                }
            }
            component.set('v.gridData', row);
            component.set('v.PageNumber',currentPage+1);
            component.set('v.TotalPages',totalPage);
            component.set('v.gridExpandedRows', expandedRows);
        }
    })
    

    OUTPUT: 


    If you have any questions you can reach out our Salesforce Consulting team here.

    Thursday 1 April 2021

    Automatically create new contact from Email-To-Case

    Requirement : 
    While working on one of the requirement of Commerce sector project for a client based out GA, USA; there was a requirement to build support system which can automatically create new case and task for Contact. For new Contact,  it should create the new contact first in Salesforce and then create new case and task under it. 


    Challenge : 
    Salesforce automatically creates Case from Email. It is called Email-to-Case. This feature also creates tasks along with the Case. Hence the first half of the requirement is solved using the Salesforce standard feature provided by Salesforce Service Cloud. The main challenge for us was to create a new contact whenever we receive an email from a new email address because Salesforce doesn't provide this feature in Email-to-Case. Although, we can install the Email-to-Case Premium package as additional subscription which requires license.

    Solution : 
    Instead of subscription, We have used the Salesforce Service Cloud provided Email-to-Case feature to create a new case from an e-mail and Apex trigger to create new contact if email address is not exist. 

    We can use Email-to-Case in 2 different ways : 
    1. Email-to-Case 
    2. On-demand Email-to-Case
    The main difference between these two is Email-to-Case accepts the emails larger than 25 MB while On-Demand Email-to-Case accepts the emails less than 25 MB.

    Note : Once you enable Email-to-Case, you cannot disable it. However, you can disable the On-Demand Service.

    To setup Email-to-Case service follow the link Email-to-Case. Once setup is completed you will have configuration as shown below.


    Now, you also need to configure routing address for the account which is being used as Customer Support in your organization.






    Now, you will be having a Salesforce generated dynamic email address that can be used to create the Case.

    Since the generated email address is too long to remember, you can configure an email routing address at your support account, so that the email received to your customer support Account, will be forwarded to the Salesforce.

    Please refer the Email routing to configure forwarding the email to Salesforce generated dynamic email address. 


    For second part of the requirement, to create new contact for the unknown/new email address received by salesforce, we have created a Apex trigger on Case Object. Refer below code for that.  

    The trigger has the mechanism to check whether received email address is exist in contacts objects or not.  

    If the contact exists with that email address, the case will be assigned to that contact.  

    If the contact doesn’t exist, a new contact will be created and then the case will be assigned to that contact. 

    Trigger to create contact : 

    trigger TriggertoCreateContactforCase on Case (before insert) {
        if(Trigger.isBefore){
            if(Trigger.isInsert){
                List<String> insertedEmailAddresses = new List<String>();
                // Create a list of email addresses for newly inserted case where contact is not set
                for (Case cs:Trigger.new) {
                    if (cs.ContactId==null && cs.SuppliedEmail!='' || cs.SuppliedEmail!=null) {
                        insertedEmailAddresses.add(cs.SuppliedEmail);
                    }
                }          
                
                Set<String> exstingEmailSet = new Set<String>();
                if(insertedEmailAddresses.size() > 0){
                    // Create a set of Email address which are already available in contact
                    for (Contact c:[Select Id,Email From Contact Where Email in:insertedEmailAddresses]) {
                        exstingEmailSet.add(c.Email);
                    }
                }
                
                Contact contTemp = new Contact();
                List<String> Emailheader = new List<String>();
                List<Case> casesToUpdateList = new List<Case>();
                Map<String,Contact> emailToContactMap = new Map<String,Contact>();
                
                // For all the cases with a null contactId, We will create a contact
                for (Case c:Trigger.new) {
                    if (c.ContactId==null && c.SuppliedName!=null && c.SuppliedEmail!=null && c.SuppliedName!='' &&
                        !c.SuppliedName.contains('@') && c.SuppliedEmail!='' && !exstingEmailSet.contains(c.SuppliedEmail)) {
                            Emailheader = c.SuppliedName.split(' ',2);
                            if (Emailheader.size() == 2) {
                                contTemp.FirstName=Emailheader[0];
                                contTemp.LastName=Emailheader[1];
                                contTemp.Email=c.SuppliedEmail;
                                emailToContactMap.put(c.SuppliedEmail,contTemp);
                                casesToUpdateList.add(c);
                            }
                        }
                }
                
                // Inserting Contact
                List<Contact> contactList = new List<Contact>();
                if(emailToContactMap.keyset().size() > 0){
                    contactList = emailToContactMap.values();
                    insert contactList;
                }
                
                // Updating Cases
                if(contactList.size()>0) {
                    for (Case cs:casesToUpdateList) {
                        contTemp = emailToContactMap.get(cs.SuppliedEmail);
                        cs.ContactId = contTemp.Id;
                    }
                }
            }
        }
    }
    

    Summary : 

    By following the above steps, we have achieved the requirement. Now, whenever an email is received to the routing email address, it will automatically create a case and task for that case. While creating the case if the contact is not exist for that email address, the trigger will create a contact and then create a case for that contact. 

    If you have any questions you can reach out our Salesforce Consulting team here.