January 31, 2021

Scratch Org in Salesforce

Scratch Org in Salesforce helps Source Driven Development.

The scratch org is a source-driven and disposable deployment of Salesforce code and metadata, made for developers and automation (CI/CD). A scratch org is fully configurable, allowing developers to emulate different Salesforce editions with different features and preferences.

Scratch Org Expiry:
Specify the scratch org’s duration, which indicates when the scratch org expires (in days).
sfdx force:org:create -f config/project-scratch-def.json --durationdays 30



Composite Graph in Salesforce

Composite Graph in Salesforce
Composite Graph in Salesforce is similar to Composite Request. But, this has higher limit. Powerful API request to avoid multiple API calls to Salesforce.

Regular composite requests allow you to execute a series of REST API requests in a single call. And you can use the output of one request as the input to a subsequent request.

Composite graphs extend this by allowing you to assemble a more complicated and complete series of related objects and records.

Composite graphs also enable you to ensure that the steps in a given set of operations are either all completed or all not completed. This avoids requiring you to check for a mix of successful and unsuccessful results.

Regular composite requests have a limit of 25 sub-requests. Composite graphs increase this limit to 500. This gives a single API call much greater power.

Example:
Using the below body, I am creating two requests.
Request 1:
Parent Account 1(Account Record)
  Child Account 1(Child Account Record to Parent Account 1)
    Test 2 Test 2(Contact Record to Child Account 1)
  Test 1 Test 1(Contact Record to Parent Account 1)
Request 2:
  Parent Account 2(Account Record)
    Child Account 2(Child Account Record to Parent Account 1)
      Test 4 Test 4(Contact Record to Child Account 2)
    Test 3 Test 3(Contact Record to Parent Account 2)
POST Body:
{
   "graphs":[
      {
         "graphId":"1",
         "compositeRequest":[
            {
               "url":"/services/data/v50.0/sobjects/Account/",
               "body":{
                  "name":"Parent Account 1",
                  "description":"Parent account"
               },
               "method":"POST",
               "referenceId":"reference_id_account_1"
            },
            {
               "url":"/services/data/v50.0/sobjects/Account/",
               "body":{
                  "name":"Child Account 1",
                  "description":"Child account",
                  "ParentId":"@{reference_id_account_1.id}"
               },
               "method":"POST",
               "referenceId":"reference_id_account_2"
            },
            {
               "url":"/services/data/v50.0/sobjects/Contact/",
               "body":{
                  "FirstName":"Test 1",
                  "LastName":"Test 1",
                  "AccountId":"@{reference_id_account_1.id}"
               },
               "method":"POST",
               "referenceId":"reference_id_contact_1"
            },
            {
               "url":"/services/data/v50.0/sobjects/Contact/",
               "body":{
                  "FirstName":"Test 2",
                  "LastName":"Test 2",
                  "AccountId":"@{reference_id_account_2.id}"
               },
               "method":"POST",
               "referenceId":"reference_id_contact_2"
            }
         ]
      },
      {
         "graphId":"2",
         "compositeRequest":[
            {
               "url":"/services/data/v50.0/sobjects/Account/",
               "body":{
                  "name":"Parent Account 2",
                  "description":"Parent account"
               },
               "method":"POST",
               "referenceId":"reference_id_account_1"
            },
            {
               "url":"/services/data/v50.0/sobjects/Account/",
               "body":{
                  "name":"Child Account 2",
                  "description":"Child account",
                  "ParentId":"@{reference_id_account_1.id}"
               },
               "method":"POST",
               "referenceId":"reference_id_account_2"
            },
            {
               "url":"/services/data/v50.0/sobjects/Contact/",
               "body":{
                  "FirstName":"Test 3",
                  "LastName":"Test 3",
                  "AccountId":"@{reference_id_account_1.id}"
               },
               "method":"POST",
               "referenceId":"reference_id_contact_1"
            },
            {
               "url":"/services/data/v50.0/sobjects/Contact/",
               "body":{
                  "FirstName":"Test 4",
                  "LastName":"Test 4",
                  "AccountId":"@{reference_id_account_2.id}"
               },
               "method":"POST",
               "referenceId":"reference_id_contact_2"
            }
         ]
      }
   ]
}

Output:


Check the below link for additional information

How to avoid "Your chat request has been canceled because no agents are available." in Salesforce Chat?

1. Select Enable Queue under Chat Button.

2. Set the Overall Queue Size.

In my example, I have set it to 3. So, if all the agents are at 100% capacity, 3 chats can be queued. If additional Chats comes in, it will be denied.
 

January 30, 2021

How to brand Salesforce Login Page?

1. Go to My Domain.


2. Update in Authentication Configuration section.
 

Help Article for reference - https://help.salesforce.com/articleView?id=sf.domain_name_login_branding.htm&type=5

January 28, 2021

Recycle Bin Usage in Salesforce Lightning

1. Create a List View in the Recycle Bin Tab.

2. Click the Char icon.
 

3. Enter Chart Name and select the other values. Aggregate Type should be count. Click Save button to View the Chart.


Output:


January 27, 2021

liveAgent:clientChatQueuePosition in Salesforce Chat

<liveAgent:clientChatQueuePosition /> in Salesforce Chat can be used to show the Queue position.
 
Sample Code:

<apex:page showHeader="false">
    <style>
        #liveAgentClientChat.liveAgentStateWaiting {
        // The CSS class that is applied when the chat request is waiting to be accepted
        // See "Waiting State" screenshot below
        }
        #liveAgentClientChat {
        // The CSS class that is applied when the chat is currently engaged
        // See "Engaged State" screenshot below
        }
        #liveAgentClientChat.liveAgentStateEnded {
        // The CSS class that is applied when the chat has ended
        // See "Ended State" screenshot below
        }
        body { overflow: hidden; width: 100%; height: 100%; padding: 0; margin: 0 }
        #waitingMessage {
        //height: 100%;
        //width: 100%;
        //vertical-align: middle;
            //text-align: center;
            margin-left: auto;
            margin-right: auto;
        //display: none;
        }
        #liveAgentClientChat.liveAgentStateWaiting #waitingMessage { display: table; }
        #liveAgentSaveButton, #liveAgentEndButton { z-index: 2; }
        .liveAgentChatInput {
            height: 25px;
            border-width: 1px;
            border-style: solid;
            border-color: #000;
            padding: 2px 0 2px 4px;
            background: #fff;
            display: block;
            width: 99%;
        }
        .liveAgentSendButton {
            display: block;
            width: 60px;
            height: 31px;
            padding: 0 0 3px;
            position: absolute;
            top: 0;
            right: -67px;
        }
        #liveAgentChatLog {
            width: auto;
            height: auto;
            top: 0px;
            position: absolute;
            overflow-y: auto;
            left: 0;
            right: 0;
            bottom: 0;
        }
        .center {
          margin-left: auto;
          margin-right: auto;
          vertical-align: middle;
        }
    </style>
    <div style="top: 0; left: 0; right: 0; bottom: 0; position: absolute;">
        <liveAgent:clientChat>
            <liveAgent:clientChatSaveButton />
            <liveAgent:clientChatEndButton />
            <div style="top: 25px; left: 5px; right: 5px; bottom: 5px; position: absolute; z-index:
                        0;">
                <liveAgent:clientChatAlertMessage />
                <liveAgent:clientChatStatusMessage />
                <table cellpadding="10" cellspacing="10" id="waitingMessage">
                    <tr>
                        <td colspan="2">Please wait while you are connected to an available agent.</td>
                    </tr>
                    <tr>
                        <td>Your queue position is </td>
                        <td>
                            <liveAgent:clientChatQueuePosition />
                            <style>
                            span.liveAgentQueuePosition:empty::before { content: '0'; }
                            </style>
                        </td>
                    </tr>  
                </table>
                <div style="top: 0; right: 0; bottom: 41px; left: 0; padding: 0; position: absolute;
                            word-wrap: break-word; z-index: 0;">
                    <liveAgent:clientChatLog />
                </div>
                <div style="position: absolute; height: auto; right: 0; bottom: 0; left: 0; margin-right:
                            67px;">
                    <liveagent:clientChatInput /><liveAgent:clientChatSendButton />
                </div>
            </div>
        </liveAgent:clientChat>
    </div>
</apex:page>

Chat button configuration:
 
 

Output:
 

January 26, 2021

'X-Frame-Options' to 'deny' issue with Force.com site

1. Go to Sites under Setup.


2. Under Sites section, click the Site Label to open the Site.

3. Set Clickjack Protection Level to "Allow framing by any page (No protection)".
Note:  It takes at least few mins to reflect the changes. So, test it after 5 mins to view the changes in Incognito Mode.
 

January 25, 2021

How to check strings equal or not using apex in Salesforce?

1. equals()
Returns true if they are same and not null.

2. equalsIgnoreCase()
Returns true if they are same ignoring Case(Upper or Lower Case) and not null.

 
Sample Code:
String str1 = 'abc';
String str2 = 'ABC';
system.debug( str1.equals( str2 ) );
system.debug( str1.equalsIgnoreCase( str2 ) );

Output:



January 21, 2021

How to show Visualforce page inside a Lightning Web Component in Salesforce?

Sample Code:

Visualforce:

<apex:page >
    
    Record Id from LWC is {!$CurrentPage.parameters.recId}
    
</apex:page>

HTML:
<template>

    <div class="slds-box slds-theme--default">
        <iframe src={siteURL} height="100px" width="150px"></iframe>
    </div>

</template>

JavaScript:
import { LightningElement,api } from 'lwc';  

export default class SampleRecordPage extends LightningElement {

    siteURL;
    @api recordId;

    connectedCallback() {
        
        this.siteURL = '/apex/Example?recId=' + this.recordId;

    }

}

JavaScript-meta.xml:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>50.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
    </targets>
</LightningComponentBundle>

Output:


January 18, 2021

How to send Mass or Bulk SMS Messages from List View in Salesforce?

Note:
1. Digital Engagement license is required to use this feature.
2. SMS Channel should be setup in the org. Follow https://www.infallibletechie.com/2020/04/sms-channel-setup-in-digital-engagement.html for setting up.
 
Follow the below steps to send Mass or Bulk SMS Messages from List View.
 
1. Make sure the user have "Send One-to-Many Messages" Permission.

 
2. Open a List View.

3. Select the records.

4. Click Send Message button.


January 15, 2021

Custom Notification using Apex in Salesforce

In the below example Notification will be sent to the Account owner when the file is attached to their Account records.
 
Notification Type in Setup:
 
 
Sample Code:
trigger ContentDocumentLinkTrigger on ContentDocumentLink ( after insert ) {
    
       
    Set < Id > setAccIds = new Set < Id >();
    Map < Id, Id > mapAccIdContDocId = new Map < Id, Id >();
       
    for ( ContentDocumentLink objCDL : trigger.new ) {
       
        String strEntityId = objCDL.LinkedEntityId;
        
        if ( String.isNotBlank( objCDL.LinkedEntityId ) && strEntityId.left( 3 ) == '001' ) {
       
            setAccIds.add( objCDL.LinkedEntityId );
            mapAccIdContDocId.put( objCDL.LinkedEntityId, objCDL.ContentDocumentId );
           
        }
        
    }
    
    if ( setAccIds.size() > 0 ) {
    
        CustomNotificationType notificationType = [SELECT Id FROM CustomNotificationType WHERE DeveloperName = 'Desktop'];
                   
        Messaging.CustomNotification notification = new Messaging.CustomNotification();
        Set < String > recipientsIds = new Set < String >();
        notification.setTitle( 'File Attached to your Account' );
        notification.setNotificationTypeId( notificationType.Id );
    
        for ( Account objAcc : [ SELECT Id, OwnerId FROM Account WHERE Id IN: setAccIds ] ) {
       
            notification.setBody( 'File Id is ' + mapAccIdContDocId.get( objAcc.Id ) );
            notification.setTargetId( objAcc.Id );
            recipientsIds.add( objAcc.OwnerId );
            notification.send( recipientsIds );
       
        }
    
    }

}
 
Output:


send() is not bulkified. So, remember 150 DML Governor limit.

January 14, 2021

Default value for lightning:combobox in Salesforce Lightning

Sample Code:

Component:

<aura:component implements="force:appHostable" >    
    <aura:attribute name = "defaultVal" type = "String"/>
        <aura:attribute name = "options" type = "List" default = "[
            {'label': 'Apple', 'value': 'apple'},
            {'label': 'Banana', 'value': 'banana'},
            {'label': 'Grapes', 'value': 'grapes'},
        ]"/>    
    <aura:handler name = "init" value = "{!this}" action = "{!c.onInit}"/>
    
    <div class = "slds-box slds-theme_default">
        <lightning:combobox
            name="Fruit"
            label = "Select a Fruit"
            value = "{! v.defaultVal }"
            placeholder = "Select Progress"
            options="{! v.options }"
            onchange="{! c.handleChange }"/>
    </div>
</aura:component>

JavaScript Controller:
({
    onInit : function( component, event, helper ) {    
          
        component.set( "v.defaultVal", "banana" );  
          
    },
    
    handleChange: function (cmp, event) {
        
        var selectedOptionValue = event.getParam( "value" );
        alert( "Value selected is " + selectedOptionValue );
        
    }
    
});


Output:


January 13, 2021

Salesforce Language Settings for Embedded Service Chat

embedded_svc.settings.language should be adjusted to display the Chat in the specified language.

For example, embedded_svc.settings.language = 'fr' will display the information in French.


 

January 12, 2021

Using Wrapper Class in Salesforce Lightning Web Component

Sample Code:

Apex Class:

public with sharing class AccountController {
 
    @AuraEnabled( cacheable = true )
    public static AccountWrapper fetchAccounts() {
        
        AccountWrapper wrap = new AccountWrapper();
        
        wrap.listAcc =  [ SELECT Id, Name, Industry, AccountNumber
                               FROM Account
                             LIMIT 10 ];
        
        wrap.accCount = wrap.listAcc.size();
        return wrap;
         
    }
    
    public class AccountWrapper {
        
        @AuraEnabled
        public List < Account > listAcc;
        @AuraEnabled
        public Integer accCount;
        
    }
     
}

HTML:
<template>

    <div class="slds-box slds-theme--default">
        <template if:true = {records}>          
            No of Accounts : {intCount}        
            <div style = "height: 300px;">
                <table class="slds-table slds-table_cell-buffer slds-table_bordered slds-table_striped">
                    <tr>
                        <th scope = "col">Name</th>
                        <th scope = "col">Industry</th>
                        <th scope = "col">Account Number</th>
                    </tr>
                    <template iterator:it = {records}>
                        <tr key = {it.value.Id}>
                            <td>{it.value.Name}</td>
                            <td>{it.value.Industry}</td>
                            <td>{it.value.AccountNumber}</td>
                        </tr>
                    </template>
                </table>
            </div>                  

        </template>      
        <template if:true = {error}>  
        {error}>                  
        </template>  
    </div>

</template>

JavaScript:
import { LightningElement, wire } from 'lwc';
import fetchAccounts from '@salesforce/apex/AccountController.fetchAccounts';

 

export default class Sample extends LightningElement {

    records;
    error;
    intCount

    @wire( fetchAccounts )  
    wiredAccount( { error, data } ) {

        if ( data ) {

            console.log( 'Data from Apex' + JSON.stringify( data ) );
            this.records = data.listAcc;
            this.intCount = data.accCount;
            this.error = undefined;

        } else if ( error ) {

            this.error = error;
            this.records = undefined;

        }

    }

}

JavaScript-meta.xml:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>49.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__Tab</target>
    </targets>
</LightningComponentBundle>

Output:
 

January 11, 2021

Salesforce Release Planning Best Practices

1. Join the Release Readiness Trailblazers Chatter Group.
https://trailblazers.salesforce.com/_ui/core/chatter/groups/GroupProfilePage?g=0F9300000001oku

2. Create a Pre-Release org to check the new features.

3. Check the Salesforce Release Notes which are published few months ago the Release.

4. Give special attention to product changes and Critical Updates from the Release Notes.

5. Schedule a meeting with the Stake Holders and Business Owners and demo the Out of the Box features that helps their Agents/Users in their day-to-day activities.

Check the Sandbox rollout schedule and do a regression testing in the Sandbox well in advance.

After the production schedule, do a thorough regression testing.

Trailhead Resources
1. https://trailhead.salesforce.com/en/content/learn/modules/sf_releases
2. https://trailhead.salesforce.com/content/learn/modules/advanced-salesforce-release-readiness-strategies

Time spent at Capacity in Salesforce Chat Session Report

Total amount of time in seconds in which an agent’s queue was full.
This field would capture the value of the time (seconds) the agent is at full capacity which is 100%.
This field will be blank if the agents never reached 100% capacity.

Chat Session Reports - https://help.salesforce.com/articleView?id=reports_live_agent.htm&type=5
 
1. Create a Custom Report Type with Chat Session as primary object.
 

2. Add Time spent at Capacity to the report.


January 9, 2021

Dynamic href to list of records in LWC Salesforce

LWC:

HTML:

<template>

    <div class="slds-box slds-theme--default">
        <template if:true = {records}>                  
                <div style = "height: 300px;">
                    <table class="slds-table slds-table_cell-buffer slds-table_bordered slds-table_striped">
                        <tr>
                            <th scope = "col">Name</th>
                            <th scope = "col">Industry</th>
                            <th scope = "col">Account Number</th>
                        </tr>
                        <template iterator:it = {records}>
                            <tr key = {it.value.Id}>
                                <td><a href={it.value.hrefVal}>{it.value.Name}</a></td>
                                <td>{it.value.Industry}</td>
                                <td>{it.value.AccountNumber}</td>
                            </tr>
                        </template>
                    </table>
                </div>                  
     
            </template>      
            <template if:true = {error}>  
                {error}>                  
            </template>  
    </div>
    
</template>

JavaScript:
import { LightningElement, wire } from 'lwc';
import fetchAccounts from '@salesforce/apex/AccountController.fetchAccounts';
import { NavigationMixin } from 'lightning/navigation';

 

export default class Sample extends NavigationMixin( LightningElement ) {

    records;
    error;

    @wire( fetchAccounts )  
    wiredAccount( { error, data } ) {

        if ( data ) {

            let rows = [];
            let tempRows = JSON.parse( JSON.stringify( data ) );

            for ( let i = 0; i < tempRows.length; i++ ) {

                let row =  tempRows[ i ];
                console.log( "Element value is " + JSON.stringify( row ) );
                row.hrefVal = "/apex/Example?recId=" + row[ "Id" ] + "&checkBool=true";
                rows.push( row );
                
            }

            this.records = rows;
            this.error = undefined;

        } else if ( error ) {

            this.error = error;
            this.records = undefined;

        }

    }

}

JavaScript-meta.xml:
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>49.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__Tab</target>
    </targets>
</LightningComponentBundle>

Visualforce page:
<apex:page >
    
    {!$CurrentPage.parameters.recId}<br/>
    {!$CurrentPage.parameters.checkBool}
    
</apex:page>

Output:



January 8, 2021

Probably Limit Exceeded or 0 recipients Exception in Salesforce

Possible scenarios are
1. If you send an email to the user who is inactive.

2. If the Email Alert is set to Owner and Owner is Queue with no members in it.

Generate Debug Log and find the email alert recipient value.

 
Best Practices:
1. Make sure to check whether the user is active in the Process Builder, Flow, Workflow, etc before sending the Email Alert.

2. Don't have queue where there are not active Users.

Why .invalid is added to Users email addresses after Sandbox Refresh in Salesforce?

When you create, refresh, or clone a sandbox, user email addresses are modified in your sandbox so that users won't accidentally receive email while doing development and testing in the Sandbox environment.
 
While doing development and testing, the existing Process Builders, Workflow Rules and Triggers may send emails to the users. In order to avoid this chaos, emails sent to email addresses ending with .invalid are ignored by the system. 

Note:
1. User who refreshed the Sandbox email address won't be appended with .invalid.
2. Email addresses configured in Custom Settings, Custom Metadata Types won't be appended. 
3. If you stuck with logging in, check this article - https://help.salesforce.com/articleView?id=000335702&type=1&mode=1

For additional information, please check the below reference link

January 7, 2021

Batch Request in Salesforce

Executes up to 25 subrequests in a single request. The response bodies and HTTP statuses of the subrequests in the batch are returned in a single response body. Each subrequest counts against rate limits unlike Composite Request.

Subrequests execute serially in their order in the request body. When a subrequest executes successfully, it commits its data. Commits are reflected in the output of later subrequests. If a subrequest fails, commits made by previous subrequests are not rolled back. If a batch request doesn’t complete within 10 minutes, the batch times out and the remaining subrequests aren’t executed.

haltOnError
Controls whether Salesforce should stop processing subrequests if a subrequest fails. The default is false.
If the value is false and a subrequest in the batch doesn’t complete, Salesforce attempts to execute the subsequent subrequests in the batch.
If the value is true and a subrequest in the batch doesn’t complete due to an HTTP response in the 400 or 500 range, Salesforce halts execution. It returns an HTTP 412 status code and a BATCH_PROCESSING_HALTED error message for each subsequent subrequest. 
 
Different between Composite and Batch in Composite Resources
Composite

In a subrequest’s body, you specify a reference ID that maps to the subrequest’s response. You can then refer to the ID in the url or body fields of later subrequests by using a JavaScript-like reference notation.

allOrNone flag is available for roll back option.

Batch
Subrequests are independent, and you can’t pass information between them. Subrequests execute serially in their order in the request body.

If a batch request doesn’t complete within 10 minutes, the batch times out and the remaining subrequests aren’t executed.

January 6, 2021

Queue-Based Chat Routing in Salesforce Lightning

1. Go to Omni-Channel Settings under Setup.
 

 
2.  Select Enable Omni-Channel and click Save Button.


3. Go to Chat Settings under Setup.


4. Select Enable Chat and click Save Button.


5. Go to Skills under Setup.


6. Click New Button.


7. Create a new Skill. Make sure to select Users or Profiles for the Skill.


8. Open Routing Configurations under Setup.
 
 
9. Click New button.
 

10.  Create a new Routing Configuration.


11. Open Queues under Setup.
 

12. Click New button.


13. Save the Queue information.

a. Select the Routing configuration created in Step 10.
b. Select Chat Transcript as Supported object.
c. Select the Queue Members.
 

14. Open Chat Buttons & Invitations under Setup.


15. Click New button.


16. Save the Chat Button information.

a. Select Type as Chat Button.
b. Routing type should be Omni-Channel.
c. Select the Queue created in Step 13.


17. Copy the Code from "Chat Button Code" section.

18. Paste it in a text file.

19. Replace <!-- Online Chat Content --> and <!-- Offline Chat Content -->.
 
20. Go to Deployments under Chat Setup.
 
 
21. Click New Button.


23. Save the Deployment information.
 
 
24. Copy the Code from "Deployment Code" section.
 
25. Paste it in the same text file below the Chat Button Code.

26. Save it with any name and add .html as extension.

27. Go to Presence Statuses under setup.


28. Click New button.


29. Save the Presence Status information.


30. Go to App Manager under setup.


31. Edit the App(Console Navigation App).

32. Add Omni-Channel under Utility Items(Desktop Only).


33. Add the Presence Status created in Step 29 to the Profile. Add it to the Service Presence Statuses Access section.


34. Open the App which was edited in Step 22.

35. Select the Omni-Channel widget and select Chat.


36. Make sure you are Online.


37. Open the HTML file which contains Deployment Code and Chat Component Code.

38. Click the Link to chat with the user.


39. Go to the Salesforce App and Accept the Chat.


40. Agent and Visitor can chat with each other.