June 25, 2021

Salesforce Inbound Email Service - Capturing From Address, To Address and Subject

Sample code:
global class CreateEmailLog implements Messaging.InboundEmailHandler {

    global Messaging.InboundEmailResult handleInboundEmail( Messaging.InboundEmail email, Messaging.InboundEnvelope env ) {
   
        Messaging.InboundEmailResult result = new Messaging.InboundEmailResult();
           
        try {
            
            Inbound_Email__c objIE = new Inbound_Email__c();           
            objIE.Email_Body__c = email.plainTextBody;
            objIE.From_Address__c = env.fromAddress;
            objIE.To_Address__c= env.toAddress;
            objIE.Subject__c= email.Subject;
            insert objIE;   
           
        } catch ( Exception e ) {
        
            System.debug('Error is: ' + e.getMessage() );
            
        }  
         
        result.success = true;        
        return result;
        
    }
    
}
 
Output:


How to end the Messaging Session using Einstein Bot and Business Hours in Salesforce?

1. Create a Business Hours.


2. Configure the Business Hours in the Messaging Channel.


3. Use the No Agent dialog in the Einstein Bot to notify the customers.

 
Note:
1. I am Using Rules and End Chat to end the Messaging Session immediately. So, after sending "Contact us Mon-Fri 9 AM to 4 PM", it will auto end the Messaging Session.

Output:
 


 
If  you want to create a Case record to capture details and log, follow the below
 
1. Create a Auto Launched Flow to create a Case Record.
 

Flow Variables:
 
 
objCase:
 
strCaseNumber:
 
strDescription:
 
 
Case Assignment:
 

Create Case: 


Get Case Number:
 

 

Case Number Assignment:
 

 
Bot configuration:
 
 
 
Output:
 

June 24, 2021

Digital Engagement Messaging Usage in Salesforce

 
(Or)

1. Go to Company Information in Setup.

 
2. Navigate to the "Usage-based Entitlements". Check the below Entitlements.

a. Maximum Billed Blast Conversations for Messaging
b. Maximum Billed Agent Conversations for Messaging


MaxBilledAgent is the actual conversations.
MaxBilledBlast is the messages triggered by Process Builder or Flow.
 

June 23, 2021

System.DmlException: Insert failed. First exception on row 0; first error: FIELD_INTEGRITY_EXCEPTION, field integrity exception: [] Salesforce PSR Creation

Error Message:
{
        "message": "field integrity exception",
        "errorCode": "FIELD_INTEGRITY_EXCEPTION",
        "fields": []
}


The creation of PSR(PendingServiceRouting) for external or queue-based routing from apex is not supported.
 
It is applicable only for Skill based as of now.

June 19, 2021

How to visualize Debug Log using VS Code for Salesforce?

1. Generate the Debug Log as usual from Debug Logs under Setup.

2. Use Get Apex Debug Logs in VS Code.


3. Select the Log from the retrieved logs.

4. Right click the log and select Show Log Analysis.


For SOQLs Run time, check the Call Tree. Check the run time next to the SOQL.


Salesforce Interview Questions with Answers Part 67

1. Secondary Routing Priority in Salesforce Omni-Channel
In a queue, priority is determined by how long the work item has been waiting—first in, first out. However, while a work item is pending in the queue, new work could come in that is more urgent. In this Case, Secondary Routing Priority in Salesforce Omni-Channel plays a vital role.
 

2. Is Heroku PaaS or IaaS?

Heroku is a Platform as a Service which runs on AWS. But, managed by Salesforce.

3. Heroku Connect and Salesforce API Limits
Heroku Connect data sync does not count against Salesforce API limits.
By default, Heroku Connect will poll your Salesforce org for changes to sync every ten minutes. It also offers accelerated polling for some objects.
Accelerated Polling(Push Topic) - https://help.heroku.com/0GKMRNAK/can-i-use-accelerated-polling-for-all-heroku-connect-mapping-objects

4. Heroku Pipeline
A pipeline is a group of Heroku apps that share the same codebase. Each app in a pipeline represents one of the following stages in a continuous delivery workflow:

- Development
- Review
- Staging
- Production

Pipelines are extremely useful for managing multiple environments for your app. A common pipeline workflow has the following steps:

- A developer creates a pull request to make a change to the codebase.
- Heroku automatically creates a review app for the pull request, allowing developers to test the change.
- When the change is ready, it’s merged into the codebase’s master branch.
- The master branch is automatically deployed to the pipeline’s staging app for further testing.
- When the change is ready, a developer promotes the staging app to production, making it available to the app’s end users.

5. What is Pardot in Salesforce?
Pardot is Salesforce’s B2B marketing automation solution. If you’re a business who sells to other businesses (or B2B), Pardot can automate your marketing activities and unite your marketing and sales departments so that they can work better together.

6. Difference Between Pardot Campaigns and Lists
Pardot campaigns are prospects grouped together for tracking and reporting purposes.
 
Pardot lists are prospects grouped together to make it easier to email marketing materials to them as a group.

7. "Edit Read Only Fields" permission 
"Edit Read Only Fields" permission allows users to edit values in the fields that are read only due to page layouts or field-level security.

8. Will Role-Hierarchy grants records shared via Sharing Set?
No. Record access granted to users via sharing sets isn’t extended to their superiors in the role hierarchy.
https://www.infallibletechie.com/2014/02/systemlimitexception-apex-cpu-time.html

10. How to restrict Salesforce Lightning Aura Component for objects and device?
Sample component’s design resource for restricting it to two objects and available for both desktop and phone support added.

<design:component label="Sample Component">
    <design:attribute name="title" label="Title" description="Title for lightning card" />
    <design:supportedFormFactors>
        <design:supportedFormFactor type="Large"/>
        <design:supportedFormFactor type="Small"/>
    </design:supportedFormFactors>
    <sfdc:objects>
        <sfdc:object>Custom_Object__c</sfdc:object>
        <sfdc:object>Account</sfdc:object>
    </sfdc:objects>
</design:component>


11.  How to call apex class when a file is downloaded in Salesforce? 
Use Sfc.ContentDownloadHandlerFactory interface to call apex class when a file is downloaded in Salesforce.
 
Note:
When a download is triggered either from the UI, Connect API, or an sObject call retrieving ContentVersion.VersionData, implementations of the Sfc.ContentDownloadHandlerFactory are looked up.
 
System.debug( 'Value is ' + [ SELECT Id, VersionData FROM ContentVersion WHERE Id = '0684x00000377ZUAAY' ] ); 
If the above code is executed, then also the apex class will be called.
 
But, if we use the below REST API Endpoint, then the apex class won't be invoked.
/services/data/v51.0/sobjects/ContentVersion/0684x00000377ZUAAY

12. Salesforce Products are not visible in the Price Book
A price book is a list of products and their prices.
 
To make Products are visible for a Price Book
 
1. Add the Products to the Price Book and enter the Amount.
 
2. Active checkbox in Price Book decides whether you can add the price book to an opportunity or quote. So, Price Book record for the product should be Active.


13. How to assign the Chat to other available agents when the agent is not accepting the Chats in Salesforce?
Use Push Time-Out.
 
Push Time-Out is the amount of time given to an agent to respond to an assigned item (Chats or other records when using Omni-Channel) before it’s pushed to another agent. This can be set with Chat Routing Information and with Omni-Channel Routing Configurations. If both are configured with a different a value (time in seconds) the time set for Chat takes precedence over Omni-Channel routing configuration.

https://help.salesforce.com/articleView?id=000313054&mode=1&sfdcIFrameOrigin=null&type=1
 
14. How to find from where the Salesforce Canvas App is loaded?
When you add your canvas app as a connected app in Salesforce, you can retrieve information about the current environment by using the Context object. The Context object provides information to your app about how and by whom it’s being consumed. You can use this information to make subsequent calls for information and code your app so that it appears completely integrated with the Salesforce user interface.

Salesforce Related Record Component Exception

If  you get We can’t display this information. Ask your admin for help or any other exception from Related Record Component, please check the below.
 
1. Find the Quick Actions added to the Related Record Component. Check whether the user have login access to all the fields added to the Quick Action layout.
 
2. In the Quick Action layout, if there are any lookup fields, make sure the user have access to the records linked through that lookup fields.

Agent Offline issue when Einstein Bot is configured in Salesforce

1. Make sure "Require Agent Online" is Off.


2. Check  "Permitted Domains" in the Chat Deployment.

Company Website URL to test from the Website
www.infallibletechie.com

Visualforce Domain URL to test from Visualforce page
infallibletechi2-dev-ed--c.visualforce.com

Domain URL to test from Bot Builder
infallibletechi2-dev-ed.my.salesforce.com 


June 18, 2021

How to calculate Agent total spent time on Chat in Salesforce?

1. Create a Report Type on Agent Work entity.
 
 
2. Create a formula field on the report to calculate the duration in Seconds.
 
Formula for reference:
( TIMEVALUE( AgentWork.CloseDateTime )-TIMEVALUE( AgentWork.AcceptDateTime ) ) / 1000 
 

June 16, 2021

Salesforce Pre Chat for Embedded Service using Lightning Aura Component

Sample Code:

Aura Component:

<aura:component implements="lightningsnapin:prechatUI">
    <lightningsnapin:prechatAPI aura:id="prechatAPI"/>
    <aura:handler name="init" value="{!this}" action="{!c.onInit}" />
    <aura:attribute name="startBool" type="Boolean" default="false"/>
    <aura:attribute name="errorBool" type="Boolean" default="false"/>
    <aura:attribute name="firstName" type="Boolean" default="false"/>
    <aura:attribute name="lastName" type="Boolean" default="false"/>
    <aura:attribute name="email" type="Boolean" default="false"/>
    <aura:attribute name="subject" type="Boolean" default="false"/>
    <aura:attribute name="prechatFieldComponents" type="List" description="An array of objects representing the pre-chat fields specified in pre-chat setup."/>
    <h2>Prechat form</h2>
    <div>
        Claims FAQs - <a href="www.infallibletechie.com">Click Here</a><br/>
        Payment FAQs - <a href="www.infallibletechie.com">Click Here</a><br/><br/><br/>
        <ui:button label="Want to Chat with our Agent?" press="{!c.onHelpButtonClick}"></ui:button><br/><br/><br/>
        <aura:if isTrue="{!v.startBool}">
            <div>
                <table cellspacing="10" cellpadding="10" width="100%">
                    <aura:iteration items="{!v.prechatFieldComponents}" var="field">                        
                        <tr>{!field}<br/></tr>   
                    </aura:iteration>
                </table>
                <br/>
                <aura:if isTrue="{!v.errorBool}">
                    <div class="errorMessage">
                        Please fill in all the required fields
                    </div>
                </aura:if>
                <ui:button aura:id="startButton" label="Start Chat" press="{!c.handleStartButtonClick}"/>
            </div>
        </aura:if>
    </div>
</aura:component>

JavaScript:
({
    
    onInit : function( component, event, helper ) {
        
        var prechatFields = component.find( "prechatAPI" ).getPrechatFields();
        var prechatFieldComponentsArray = helper.getPrechatFieldAttributesArray( prechatFields );
        $A.createComponents(
            prechatFieldComponentsArray,
            function( components, status, errorMessage ) {
                if ( status === "SUCCESS" ) {
                    
                    component.set( "v.prechatFieldComponents", components );
                    
                }
            }
        );
        
    },
    
    handleStartButtonClick: function( component, event, helper ) {
        
        helper.onStartButtonClick( component );
        
    },
    
    onHelpButtonClick:function( component ) {
        
        component.set( "v.startBool", "true" );
        
    }
    
})

Helper:
({
    
    fieldLabelToName: {
        "First Name": "FirstName",
        "Last Name": "LastName",
        "Email": "Email",
        "Subject": "Subject"
    },
    
    getPrechatFieldAttributesArray: function( prechatFields ) {
        
        var prechatFieldsInfoArray = [];
 
        prechatFields.forEach( function( field ) {
            
            var componentName = ( field.type === "inputSplitName" ) ? "inputText" : field.type;
            var componentInfoArray = [ "ui:" + componentName ];
            var attributes = {
                "aura:id": "prechatField",
                required: field.required,
                label: field.label,
                disabled: field.readOnly,
                maxlength: field.maxLength,
                class: field.className,
                value: field.value
            };
            
            if ( field.type === "inputSelect" && field.picklistOptions )
                attributes.options = field.picklistOptions;
            
            componentInfoArray.push( attributes );
            prechatFieldsInfoArray.push( componentInfoArray );
            
        });
 
        return prechatFieldsInfoArray;
    },
    
    onStartButtonClick: function( component ) {
        
        var prechatFieldComponents = component.find( "prechatField" );
        var fields;
        fields = this.createFieldsArray( prechatFieldComponents );
        
        if ( component.find( "prechatAPI").validateFields( fields ).valid ) {
            
            if ( fields[ 0 ].value && fields[ 1 ].value && fields[ 2 ].value && fields[ 3 ].value ) {
            
                component.find( "prechatAPI").startChat( fields );
                
            } else {
                
                component.set( "v.errorBool", "true" );
                
            }
            
        } else {
            
            console.warn( "Prechat fields did not pass validation!" );
            
        }
        
    },
 
    createFieldsArray: function( fields ) {
        
        if( fields.length ) {
            
            return fields.map( function( fieldCmp ) {
                return {
                    label: fieldCmp.get( "v.label" ),
                    value: fieldCmp.get( "v.value" ),
                    name: this.fieldLabelToName[ fieldCmp.get( "v.label" ) ]
                };
            }.bind( this ) );
            
        } else {
            
            return [];
            
        }
        
    }
    
})

CSS:
.THIS .uiLabel {
    font-weight: bold;
}

.THIS .errorMessage {
    color: red;
    font-weight: bold;
}


Output:


Calling Apex from Salesforce PreChat API

Sample Code:
<aura:component implements="lightningsnapin:prechatUI" controller="PreChatController">
 
If you are using Site, then do the below steps.
 
1. Go to the Site.
 
2. Click Public Access Settings.
 
3. Add the Apex Class to the Apex Class Access.
 

If you are using Experience/Community Cloud, then do the below steps.
 
1. Go to Builder.
 
2. Go to Settings.
 
3. Go to General and open the Community Profile.
 
 
4. Add the Apex Class to the Apex Class Access.

June 15, 2021

How to deploy Remote Site Setting using ANT tool in Salesforce?

Sample package.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Test</members>
        <name>RemoteSiteSetting</name>
    </types>
    <version>50.0</version>
</Package>

June 14, 2021

Knowledge Field History Tracking in Salesforce

 1. Enable "Track Field History".


2. Use Set History Tracking.


Find users part of Permission Set License in Salesforce

Syntax:
SELECT Id, UserName FROM User WHERE Id IN ( SELECT AssigneeId FROM PermissionSetLicenseAssign WHERE PermissionSetLicense.MasterLabel = '<Name of the Permission Set License' )
 
You can get the Name of the Permission Set License from Company Information.
 
Sample:
SELECT Id, UserName FROM User WHERE Id IN ( SELECT AssigneeId FROM PermissionSetLicenseAssign WHERE PermissionSetLicense.MasterLabel = 'Messaging User' ) 
 
 The above query returns all the users who have Messaging User Permission Set License.

June 11, 2021

Block Visitor or Spam Block from Chat Salesforce

1. Create a Chat Agent Configuration.

2. Enable "Visitor Blocking Enabled".


3. Add Agents or their profiles to it.
 
4. Now agents can view Block icon.
 
 


Files attached to Messages in Salesforce Messaging

Files are stored in ContentDocument entity. 
 
Use this soql to find the ContentDocumentId. 
SELECT ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId = '<MessagingSessionId>'
 
Example:
1. Run the below SOQL.
SELECT ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId = '0Mw3h00000146TkCAI'
 
 0Mw3h00000146TkCAI is the Messaging Session Id.

2. Use the ContentDocumentId and open the file.


June 8, 2021

Drop Additional Skills Time-Out (seconds) in Salesforce Skill-Based Routing

Drop Additional Skills Time-Out (seconds) in Salesforce Skill-Based Routing

Set the time to elapse before additional skills are dropped from Omni-Channel routing.


For example, lets take two Skills SkillA and SkillB are used. Once all the agents with SkillA and SkillB skills are at maximum capacity, Omni-Channel will wait for 120 seconds until it drops the SkillA skill and just look for agents with SkillB skill then assign the record(Chat, Case, Messaging, etc.) to them.

June 6, 2021

Sample Salesforce Canvas App running Locally

1. Download the below Project and extract it.

2. Move it to a directory. In my case, I have moved it to Documents folder.

3. Install GIT.

4. Install Maven. Make sure Java JDK is installed before installing Maven.

5. Navigate to SalesforceCanvasFrameworkSDK folder.


6. Use the below command to package the app.
mvn package

Your build should succeed.


7. Enter the below command.
keytool -keystore keystore -alias jetty -genkey -keyalg RSA

Enter 123456 as the password and enter other details asked.

8. Use chmod +x target/bin/webapp command if you are using Mac or Linux OS. Else, skip this step.

9. Run the below command.
sh target/bin/webapp

Test by checking "https://localhost:8443/examples/hello-world/index.jsp"

10. Go to App Manager in Salesforce. Click New Connected App.

Logo Image URL - https://localhost:8443/images/salesforce.png
Icon URL - https://localhost:8443/examples/hello-world/logo.png
Selected OAuth Scopes - Access your basic information (id, profile, email, address, phone)
Callback URL - https://localhost:8443/sdk/callback.html
Canvas App URL - https://localhost:8443/examples/hello-world/index.jsp
Access Method - Signed Request (POST)
Locations - Chatter Tab

11. Go to Chatter Tab in Classic and click the Canvas App Name to view the app.

June 4, 2021

How to assign the Chat to other available agents when the agent is not accepting the Chats in Salesforce?

Use Push Time-Out.
 
Push Time-Out is the amount of time given to an agent to respond to an assigned item (Chats or other records when using Omni-Channel) before it’s pushed to another agent. This can be set with Chat Routing Information and with Omni-Channel Routing Configurations. If both are configured with a different a value (time in seconds) the time set for Chat takes precedence over Omni-Channel routing configuration.

https://help.salesforce.com/articleView?id=000313054&mode=1&sfdcIFrameOrigin=null&type=1
 
Routing Configuration:
 

As per the above Routing Configuration, the chat will be routed to other available agent if the currently assigned doesn't accept the Chat in 10 seconds.

Dynamic Picklist using lightning combobox in Lightning Web Component in Salesforce

Sample Code:
 
HTML:
<template>
    <lightning-card>
        <div style="width:200px; padding:0.5rem;">
            <lightning-combobox
                name="filter"
                label="Industry"
                value={selectedValue}
                options={options}
                onchange={handlePicklistChange}
                placeholder="Select Industry">
            </lightning-combobox>
        </div>
    </lightning-card>
</template>

JavaScript:
import { LightningElement, wire } from 'lwc';
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';
import { getPicklistValues, getObjectInfo } from 'lightning/uiObjectInfoApi';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';

export default class SampleLWC extends LightningElement {

    options = [];
    picklistOptions = [];
    selectedValue;

    @wire( getObjectInfo, { objectApiName: ACCOUNT_OBJECT } )
    objectInfo;

    @wire( getPicklistValues, { recordTypeId: '$objectInfo.data.defaultRecordTypeId', fieldApiName: INDUSTRY_FIELD } )
    wiredData( { error, data } ) {

        console.log( 'Inside Get Picklist Values' );

        if ( data ) {
                            
            console.log( 'Data received from Picklist Field ' + JSON.stringify( data.values ) );
            this.options = data.values.map( objPL => {
                return {
                    label: `${objPL.label}`,
                    value: `${objPL.value}`
                };
            });
            console.log( 'Options are ' + JSON.stringify( this.options ) );

        } else if ( error ) {

            console.error( JSON.stringify( error ) );

        }

    }

    handlePicklistChange( event ) {
        
        console.log( 'New Value selected is ' + JSON.stringify( event.detail.value ) );   

    }

}

Output:
 
 

June 3, 2021

How to get Chat Transcript fiel value in Salesforce Embedded Chat?

1. Create a Custom Field in Chat Transcript object.
 
 
2. Create the same Custom Field in Contact object.
 
 
3. Add the Custom field from Contact to the Embedded Service Chat Deployment.
 
 

For you Website:
        embedded_svc.settings.extraPrechatFormDetails = [ {
            "label": "Skill",
            "transcriptFields":[ "Skill__c" ],
            "displayToAgent": true
        } ];

 
For Community or Experience Cloud:
1. Create a Static Resource(JavaScript File).
 
window._snapinsSnippetSettingsFile = ( function() {

    console.log( "Static Resource file loaded" );

    embedded_svc.snippetSettingsFile.extraPrechatFormDetails = [ {
        "label": "Skill",
        "transcriptFields":[ "Skill__c" ],
        "displayToAgent": true
    } ];
    
})();
 
2. Note the Static Resource Name.
 
3. Configure it in the Experience Builder. Use "Snippet Settings File".
 
 

Output: