April 30, 2020

Using Images in LiveMessage

Click Image Icon.


To send images to the customer, use Attach Image.

To get images from the customer, use Request Image.


April 29, 2020

Enable and Disable Next Button in Flow in Salesforce

With custom component, we can enable and disable buttons in Flow.

Sample Flow:




Screen - First and Last Name:
We get the First and Last name from the user.




Assignment - Assign Lead Name:
We are assigning values from Screen Component to objLead Record variable's FirstName and LastName.


Screen - Lead Screen:
We are using the LWC Component to get the Company. When the Company is filled, the Next button will be enabled. When the Company is not filled, the Next button will be disabled.


Create Records - Create Lead:


Screen - Lead Created:

 

LWC HTML:

<template>

    <table cellspacing="2" cellpadding="2">
        <tr>
            <td>First Name</td>
            <td>{LeadRec.FirstName}</td>
        </tr>
        <tr>
            <td>Last Name</td>
            <td>{LeadRec.LastName}</td>
        </tr>
        <tr>
            <td>Company</td>
            <td><lightning-input type="text" placeholder="Enter the Company Name" onchange={handleChange}></lightning-input></td>
        </tr>
        <tr><td colspan="2"><br/></td></tr>
        <tr>
            <td>
                <lightning-button label="Back" variant="brand" onclick={handleBack}></lightning-button>
            </td>
            <td>               
                <lightning-button label="Next" variant="brand" onclick={handleNext} disabled={checkBool}></lightning-button>
            </td>
        </tr>
    </table>
   
</template>


LWC JavaScript:

import { LightningElement, api, track } from 'lwc';
import {FlowAttributeChangeEvent, FlowNavigationNextEvent, FlowNavigationBackEvent } from 'lightning/flowSupport';

export default class CreateLeadFlow extends LightningElement {

    @api LeadRec;
    @api Company;
    @track checkBool = true;
   
    handleNext( event ) {
       
        const nextNavigationEvent = new FlowNavigationNextEvent();       
        this.dispatchEvent(nextNavigationEvent);

    }

    handleBack( event ) {
       
        const nextNavigationEvent = new FlowNavigationBackEvent();       
        this.dispatchEvent(nextNavigationEvent);

    }
   
    handleChange( event ) {
       
        this.Company = event.target.value;
        if ( this.Company )
            this.checkBool = false;
        else
            this.checkBool = true;

    }
              
}


LWC JavaScript-meta.xml:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>48.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__FlowScreen</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__FlowScreen">
            <property name="LeadRec" label="Lead" type="@salesforce/schema/Lead" />
            <property name="Company" label="Lead Company" type="String" />
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>


Output:






LWC for Flow in Salesforce

HTML:

<template>

    <lightning-datatable key-field="Id"
                         data={contacts}
                         columns={columns}
                         hide-checkbox-column="true"
                         show-row-number-column="true"
                         onrowaction={handleRowAction}>
    </lightning-datatable>

</template>


JavaScript:

import { LightningElement, api, track } from 'lwc';
import { NavigationMixin } from 'lightning/navigation';

const actions = [
    { label: 'View', name: 'view' },
];

const columns = [  
    { label: 'Name', fieldName: 'Name' },
    { label: 'Email', fieldName: 'Email' },
    {
        type: 'action',
        typeAttributes: { rowActions: actions },
    }, 
];

export default class ContactsFlowComponent extends NavigationMixin(LightningElement) {

    @api contacts;
    @track columns = columns;
   
    connectedCallback() {

        console.log( 'Contacts are ' + JSON.stringify( this.contacts ) );
       
    }

    handleRowAction( event ) {

        const row = event.detail.row;
        this[NavigationMixin.Navigate]({
            type: 'standard__recordPage',
            attributes: {
                recordId: row.Id,
                objectApiName: 'Contact',
                actionName: 'view'
            }
        });

    }

}


JavaScript-meta.xml:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>48.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__FlowScreen</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__FlowScreen">
            <property name="contacts" label="Contacts" type="@salesforce/schema/Contact[]" />
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>


Flow:


Get Records:


Screen:



Lightning App Builder Configuration:



Output:



April 27, 2020

How to wrap text in a table in LWC in Salesforce?

Sample code:

HTML:

<template>

    <div class="slds-box slds-theme_default">
        <table class="slds-table slds-table_cell-buffer slds-table_bordered slds-table_col-bordered">
            <thead>
                <tr>
                    <th scope="col">
                        <div class="slds-truncate" title="Account Name">Name</div>
                    </th>
                    <th class="" scope="col">
                        <div class="slds-truncate" title="Account Industry">Industry</div>
                    </th>
                    <th class="" scope="col">
                        <div class="slds-truncate" title="Account Type">Type</div>
                    </th>
                    <th class="" scope="col">
                        <div class="slds-truncate" title="Account Number">Account Number</div>
                    </th>
                    <th class="" scope="col">
                        <div class="slds-truncate" title="Site">Site</div>
                    </th>
                    <th style="width:250px;">
                        <div class="slds-truncate" title="Description">Description</div>
                    </th>
                </tr>
            </thead>
            <tbody>
                <template for:each={accounts} for:item="acc">
                    <tr key={acc.Id}>
                        <td>{acc.Name}</td>
                        <td>{acc.Industry}</td>
                        <td>{acc.Type}</td>
                        <td>{acc.AccountNumber}</td>
                        <td>{acc.Site}</td>
                        <td class="slds-cell-wrap">{acc.Description}</td>
                    </tr>
                </template>
            </tbody>
        </table>
    </div>   
   
</template>


JavaScript:

import { LightningElement, wire, track } from 'lwc';
import fetchAccounts from '@salesforce/apex/ExampleController.fetchAccounts';

export default class Example extends LightningElement {

    @track accounts;
   
    @wire( fetchAccounts ) 
    caseRecord({ error, data }) { 
 
        if ( data ) { 
 
            this.accounts = data; 
 
        } else if ( error )
            console.log( 'Error is ' + JSON.stringify( error ) );
         
    }

}

 
JavaScript meta.xml:

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


Apex Class:

public with sharing class ExampleController {

    @AuraEnabled(cacheable=true)
    public static List < Account > fetchAccounts() {

        return [ SELECT Id, Name, Industry, Type, AccountNumber, Site, Description FROM Account LIMIT 10 ];
       
    }
   
}

 

Output:



April 25, 2020

Sample/Example XmlStreamWriter using Apex in Salesforce

Sample Code:

XmlStreamWriter w = new XmlStreamWriter();
w.writeStartDocument( null, '1.0' );
w.writeStartElement( null, 'test', null );
w.writeStartElement( null, 'example', null );
w.writeAttribute( null, null, 'email', 'test123@xyz.com' );
w.writeCharacters( 'sample' );
w.writeEndElement();
w.writeEndElement();
w.writeEndDocument();
String payload = w.getXmlString();
system.debug( 'Payload is ' + payload );
w.close();


Output:


April 24, 2020

Salesforce Estimated Wait Time Instead of Queue Position for a Chat Session

By default, the Chat API returns queue position information that you can relay to customers. However, you can also receive the estimated wait time in addition to the queue position. Sometimes, the estimated wait time more effectively conveys the right information to customers than a queue position number. This feature is available in API version 47.0 and later.

Use the below steps for getting the Estimated Wait Time from the Chat API.

1. Go to Chat Settings.


2. Get the Chat API.


3. Go to Deployments.



4. Open the Chat Deployment.

5. Get the Chat Deployment Id and the Version.


6. Use Postman or SOAP UI or any REST API Explorer. Use GET method and use the Endpoint URL from step 2 and add /Visitor/Availability path to it.

7. Set X-LIVEAGENT-API-VERSION to the version of the Chat Deployment from Step 5.


8. Set the below params.

org_id=Salesforce Org Id
deployment_id=id of the chat deployment(from step 5)
Availability.needEstimatedWaitTime=1
Availability.ids=id of the chat button(Got to Chat Buttons & Invitations in Setup to get the button Id. You can pass multiple ids using [id1,id2,...])



When Agent(s) is/are available:



When Agent(s) is/are not available:

April 22, 2020

Call Salesforce Flow from REST API

Input Variable:



Output Variable:



Flow:



Fetch Records:




Workbench:
Endpoint:
/services/data/vxx.0/actions/custom/flow/Flow_API_Name

Request Body:
{
  "inputs": [
    {
      "InputVariableAPIName": "InputValue"
    }
  ]
}





April 21, 2020

How to send email every one hour until the status changes in Salesforce using Process Builder?

Use Case:
Send Email to Account Owner every hour until the Account Type is Prospecting.

Custom Field:


Process Builder:


Object Criteria:



Send Email True Criteria:
When Account type is Prospect and Send Email is True, then
Time Trigger: 1 hour after last modified date
1. Send Email
2. Update Send Email check box to false.



Send Email False Criteria:
When Account type is Prospect and Send Email is False, then
Time Trigger: 1 hour after last modified date
1. Send Email
2. Update Send Email check box to true.


April 20, 2020

Salesforce Interview Questions with Answers Part 63

1. Knowledge Article Actions in Classic Vs. Lightning
Lightning Knowledge uses user profile permissions or permissions sets to give agents access to authoring actions. In contrast, Knowledge in Salesforce Classic uses public groups and article actions.

2. Knowledge User checkbox on the user detail
By default, all internal users with Read permission can read published articles.

Lightning Knowledge uses user profile permissions or permissions sets to give agents access to authoring actions. In contrast, Knowledge in Salesforce Classic uses public groups and article actions.

To do more than read articles, agents need the Knowledge User license.

To enable, check http://www.infallibletechie.com/2014/05/how-to-enable-as-knowledge-user-in.html

3. Difference between LiveMessage and Digital Engagement Messaging
Salesforce Classic LiveMessage
Salesforce Classic LiveMessage is in a managed package.
Supports Short Code.

Digital Engagement Messaging
Digital Engagement Messaging resides in Lightning and is provisioned via a Permission Set License.
Only supported in Lightning and Lightning Service Console.
Short code is not supported.

4. Debug Log Trace Entity in Salesforce
Trace flags set logging levels (such as for Database, Workflow, and Validation) for a user, Apex class, or Apex trigger for up to 24 hours.

Select Automated Process from the drop-down list to set a trace flag on the automated process user. The automated process user runs background jobs, such as emailing Chatter invitations.
Select Platform Integration from the drop-down list to set a trace flag on the platform integration user. The platform integration user runs processes in the background, and appears in audit fields of certain records, such as cases created by the Einstein Bot.
Select User from the drop-down list to specify a user whose debug logs you'd like to monitor and retain.
Select Apex Class or Apex Trigger from the drop-down list to specify the log levels that take precedence while executing a specific Apex class or trigger. Setting class and trigger trace flags doesn’t cause logs to be generated or saved. Class and trigger trace flags override other logging levels, including logging levels set by user trace flags, but they don’t cause logging to occur. If logging is enabled when classes or triggers execute, logs are generated at the time of execution.

5. Composite Request REST API in Salesforce
Executes a series of REST API requests in a single call.
The response bodies and HTTP statuses of the requests are returned in a single response body.
The entire request counts as a single call toward your API limits.

The request body contains an allOrNone flag that specifies how to roll back errors. If true, entire request is rolled back. If false, the remaining subrequests that don’t depend on the failed subrequest are executed. In either case, the top-level request returns HTTP 200 and includes responses for each subrequest.

collateSubrequests   
Controls whether the API collates unrelated subrequests to bulkify them (true) or not (false).
When subrequests are collated, the processing speed is faster, but the order of execution is not guaranteed (unless there is an explicit dependency between the subrequests).
If collation is disabled, then the subrequests are executed in the order in which they are received. Use this field if you have dependencies between your subrequests and you need to control the order of execution. If you don’t have an explicit dependency between your subrequests, then set collateSubrequests to true.

For more information, check https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_composite.htm

6. Batch Request REST API 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.

7. Common Name
The Common Name (AKA CN) represents the server name protected by the SSL certificate. The certificate is valid only if the request hostname matches the certificate common name. Most web browsers display a warning message when connecting to an address that does not match the common name in the certificate.
In the case of a single-name certificate, the common name consists of a single host name (e.g. example.com, www.example.com), or a wildcard name in case of a wildcard certificate (e.g. *.example.com).
The common name is technically represented by the commonName field in the X.509 certificate specification.

8. Maximum number of concurrent clients per Organization in Streaming API
The limit is not based on Hourly or Daily.
The limit will be released once any of the user Unsubscribe the particular PushTopic.
However, the default limit is 2000 only for Subscriber when the are doing it through Cometd.
For internal user, it should not count against the limit.

Reference Link - https://resources.docs.salesforce.com/sfdc/pdf/api_streaming.pdf

9. Default Presence Configuration in Omni-Channel
When you enable Omni-Channel in your organization, Salesforce creates a presence configuration for you, called the Default Presence Configuration. All your agents are assigned to that configuration automatically. However, you can create a presence configuration and assign individual agents to it to customize Omni-Channel settings for a subset of your agents. If you reassign agents to a custom presence configuration, they’re excluded from the Default Presence Configuration.

10. Can we do a callout from flow synchronously?
No. Like trigger, we cannot do callout from apex action in Flow.
Callout should be done asynchronously like using future method.

https://www.infallibletechie.com/2020/04/how-to-do-callout-from-flow-in.html

11. Can we do DML and Callout in the same transaction.
Yes. But, DML should be done after the Callout. We cannot do DML before a Callout.

You can’t make a callout when there are pending operations in the same transaction. Things that result in pending operations are DML statements, asynchronous Apex (such as future methods and batch Apex jobs), scheduled Apex, or sending email. You can make callouts before performing these types of operations.

12. Salesforce Data Centers
https://help.salesforce.com/articleView?id=000314281&language=en_US&type=1&mode=1

13. Assign Records Created by Guest Users to a Default Owner permission in Salesforce Community
To increase the security of your Salesforce data, set up your org so that guest users are no longer automatically the owner of records they create. Instead, when a guest user creates a record, the record is assigned to a default active user in the org, who becomes the owner.
Having an internal org user be the owner of records created by guest users is a Salesforce security best practice. While we strongly encourage you to assign a default owner, changing record ownership can affect your guest users’ ability to access records.
Although one might be able to grant Update/Delete object permissions to the guest user, whether that be via the profile or permission sets, the guest user will still not be able to edit or delete records due to the removal of write sharing access after security updates “Secure Guest User Record Access” and “Automatically Assign Records Created by Guest Users to a Default Owner”.
The overall recommendation and best practice is to address “Read” and “Create” needs with the permissions on the Guest User Profile, record read visibility via Guest User Sharing Rules, and Edits by Apex methods residing in classes running in without sharing mode.
Best Practices - https://help.salesforce.com/articleView?id=networks_guest_record_default_owner_best_practices.htm&type=5

14. Secure guest user record access setting permission in Salesforce Community
Secure guest user record access setting limits the visibility and access that guest users have to your org’s data. When it’s enabled, guest users:

Have org-wide defaults set to Private for all objects. This access level can’t be changed.
Can’t be added to queues or public groups.
Can’t be given access to records through manual sharing or Apex managed sharing.
Can be granted Read Only access to records through guest user sharing rules. Guest user sharing rules are a special type of criteria-based sharing rule and count towards the limit of 50 criteria-based sharing rules per object.

15. Renewal Forecast in Salesforce CPQ
Once renewal forecast is checked a Renewal Opportunity will be created (unless one is defined in the Renewal Opportunity field on the Contract), and a temporary "ghost" Quote and related Quote Lines are created.

This is because Salesforce CPQ must run the entire pricing engine against the renewed subscriptions, including all Price Rules and the Quote Calculator Plugin (QCP), if one is present. After pricing calculations are completed, these quote lines are synced to the Opportunity Products as if a primary quote had been created. When this sync is complete, Salesforce CPQ removes the "ghost" quote and what is left is the Opportunity and the Opportunity Products.

April 16, 2020

How to do a callout from Flow in Salesforce?

If Salesforce creates, updates, or deletes data in your org and then accesses external data in the same transaction, an error occurs. In your flow, we recommend using a separate transaction to access data in an external system. To do so, end the prior transaction by adding a screen or local action to a screen flow or a Pause element to an auto-launched flow. If you use a Pause element, don't use a record-based resume time. For example, a screen flow creates a contact and then displays a confirmation screen. Next, the flow updates the contact in the external system. The flow doesn’t fail because it uses a separate transaction to access the external data.
 
https://help.salesforce.com/articleView?id=flow_prep_bestpractices.htm&type=5

Remote Site Settings:



Flow:




Apex Class:

public class FlowController {
   
    @InvocableMethod( label='Create Contact' description='Create Contact using Title' category='Callout' )
    public static void getTitle( List < String > inputs ) {
       
        system.debug( 'Inputs is ' + inputs );
        createContact( inputs.get( 0 ) );
       
    }
   
    @future( callout=true )
    public static void createContact( String strInput ) {
       
        List < String > listInputs = strInput.split( ',' );
        HTTP h = new HTTP();
        HTTPRequest req = new HTTPRequest();
        req.setEndPoint( 'https://jsonplaceholder.typicode.com/todos/' + listInputs.get( 0 ) );
        req.setMethod('GET');
        HTTPResponse res = h.send(req); 
        JSONWrapper wrapper = ( JSONWrapper ) JSON.deSerialize( res.getBody(), JSONWrapper.class );
        /*
           If the response contains multiple values, then you can use the below code
           List < JSONWrapper > listWrapper = (List < JSONWrapper >) JSON.deSerialize(res.getBody(), List < JSONWrapper >.class);
        */
        Contact objContact = new Contact();
        objContact.LastName = wrapper.title;
        objContact.AccountId = listInputs.get( 1 );
        system.debug( 'Contact is ' + objContact );
        insert objContact;
       
    }
   
    public class JSONWrapper {
       
        public Integer userId;
        public Integer id;
        public String title;
        public Boolean completed;
       
    }
   
}


Output:


April 15, 2020

Changing Color and Logo in Salesforce Lightning Experience App

1. Go to App Manager in Setup.

2. Select an existing App or create a new App.

3. Go to App Details & Branding.


4. Select Color and Logo.


Output:


April 14, 2020

lightning-accordion - Expand and Collapsible sections in LWC

Sample Code:

HTML:

<template>

    <div class="slds-box slds-theme_default">
        <lightning-accordion class="example-accordion"
                            onsectiontoggle={handleToggleSection}
                            allow-multiple-sections-open>
            <lightning-accordion-section name="A" label="Accordion Title A">
                Test
            </lightning-accordion-section>
        </lightning-accordion> 
    </div>   
   
</template>


JavaScript:

import { LightningElement } from 'lwc';

export default class Example extends LightningElement {
   
    handleToggleSection(event) {

        console.log( 'Selected Sections ' + event.detail.openSections );

    }

}


JavaScript meta.xml: 

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



Output: