Thursday, 6 February 2020

Use Maps in Lightning Components

Most probably in Lightning Components, we only deal with Lists to retrieve data from Apex Controller. But there are many scenarios Lists are not enough to retrieve data. So, Maps are very convenient in any simple or complex scenarios. There are very less resources available on it.

So, consider an example of two object (Objects – Consumer & Product) with junction object (Consumer Product).

Relationship as below.

Consumer has Master Details to Consumer Product.
Product has Lookup to Consumer Product.

Scenario to be implement: I want to show record of Products which are associated with Consumers in tab view filtered by Product Family.


Explanation :

Below I have provided Lightning Component bundle and Apex Class.


  • Take return type of apex method as map<String,List<Consumer_Products__c>>
  • By using force:hasRecordId, I get current Consumer record Id, will query on Consumer Product to get details of Product related to Consumer.
  • Then iterate above query and put all data in 
Map<String,List<Consumer_Products__c>> CustToProd = new Map<String,List<Consumer_Products__c>>(); 

  •  In JS Controller,
 var custs = []; 
 var conts = response.getReturnValue(); 
 for(var key in conts){ 
 custs.push({value:conts[key], key:key}); //Here we are creating the list to show on UI. 
 } 
  component.set("v.FamilyMap ",custs);

  • Use FamilyMap in lightning component to iterate and show required data.
  • First iteration - To iterate over Keys from Map.
<aura:iteration items="{!v.FamilyMap}" var="FirstIteration" indexVar="key">

  • Second Iteration  - To iterate over Product List.
<aura:iteration items="{!FirstIteration.value}" var="SecondIteration" indexVar="key1">

  • Fetch the Product date by using Var - 'SecondIteration' .
{!SecondIteration.Product__r.Name}




Lightning Component - 
<aura:component controller="ConsumerProdDetails" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" access="global" >
    <aura:attribute name="CustProductDetails" type="Object" />
    <aura:handler name="init" value="{!this}" action="{!c.doinit}" />
    <aura:attribute name="FamilyMap" type="Map" />
 
    <div>
        <div class="slds-page-header">
            <h2>
                <lightning:icon class="myUniqueClass" iconName="standard:event" alternativeText="Event" />
                <span class="boldtext" title="Product 360 View">
                    <b>Product 360 View</b>
                </span>
            </h2>
        </div>
     
        <lightning:tabset  variant="default" >
            <aura:iteration items="{!v.FamilyMap}" var="FirstIteration" indexVar="key">
                <lightning:tab  label="{!FirstIteration.key}"  > 

                    <table class="slds-table slds-table--bordered slds-table--fixed-layout  slds-max-medium-table--stacked-horizontal">
                        <thead>
                            <tr class="slds-line-height_reset">
                                <th class="" scope="col">
                                    <div class="slds-truncate" title="AccountId">Account Id</div>
                                </th>
                                <th class="" scope="col">
                                    <div class="slds-truncate" title="Product Name">Product Name</div>
                                </th>
                             
                            </tr>
                        </thead>
                        <tbody>
                            <aura:iteration items="{!FirstIteration.value}" var="SecondIteration" indexVar="key1">
                                <tr class="slds-hint-parent slds-cell-wrap">
                                    <th data-label="AccountId" scope="row">
                                        <div class="slds-truncate" >
                                            <!--onclick="{!c.StmtData}"-->
                                            <a href="#" tabindex="-1">{!SecondIteration.Id}</a>
                                        </div>
                                    </th>
                                    <td data-label="Product Name">
                                        <div class="slds-truncate" ><ui:outputText aura:id="ProdName" value="{!SecondIteration.Product__r.Name}"/></div>
                                    </td>
                                </tr>
                            </aura:iteration>
                        </tbody>
                    </table>
                </lightning:tab>
            </aura:iteration>
        </lightning:tabset>
     
    </div>
</aura:component>
JS Controller :

({
doinit : function(component, event, helper) {
        var rid = component.get("v.recordId");
        var action = component.get('c.returnConsDetail');
        action.setParams({Key : rid});
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
     
                var custs = [];
                var conts = response.getReturnValue();
                for(var key in conts){
                    custs.push({value:conts[key], key:key}); //Here we are creating the list to show on UI.
                }
                component.set("v.FamilyMap ",custs);
            }
            else if (state === "ERROR") {
                var errors = response.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        console.log("Error message: " +
                                    errors[0].message);
                    }
                }
                else {
                    console.log("Unknown Error");
                }
            }
        });
        $A.enqueueAction(action);
}
})

Apex Controller :
@auraEnabled
    public static map<String,List<Consumer_Products__c>> returnConsDetail(String Key){
        Set<id> prodId = new Set<id>();
        Map<String,List<Product2>> FamToProd = new Map<String,List<Product2>>();
        Map<String,List<Consumer_Products__c>> CustToProd = new Map<String,List<Consumer_Products__c>>();
        List<Consumer_Products__c> listCustAccs = [Select Id, Product__r.Name, Product__r.Family from Consumer_Products__c where Consumer__c =:key ];
        for(Consumer_Products__c ca : listCustAccs){
            prodId.add(ca.Product__c);
        }
        for(Consumer_Products__c CA : listCustAccs){
            if(CustToProd.containskey(CA.Product__r.family)){
                CustToProd.get(CA.Product__r.family).add(CA);
            }else{
                List<Consumer_Products__c> chlist = new List<Consumer_Products__c>();
                chlist.add(CA);
                CustToProd.put(CA.Product__r.family,chlist); 
            }
        }
        return CustToProd;
    }
Now Add component to Consumer record page by Using App Builder.

Wednesday, 19 June 2019

Entitlement Processes for Salesforce



Entitlement for the cases gives you automated solution for the SLA's. To overcome user’s manual work and automate business processes to send notifications on escalations within response times.

We will go step by step to configure the Entitlements as Below.
1.       Search ‘Entitlement Settings’ from Quick search.




It’ll enable Entitlement Process for your org.

2.       Now Go to ‘Milestones’. Create new Milestone.
Example – Let’s say, Priority field on Case has values as Low, Medium and High. If Case has SLA as
A.      If Priority is ‘Medium’ on case and the case is not Closed in next 8 hrs then it will escalate and send the Email notification.
B.      If Priority is ‘High’ on case and the case is not Closed in next 4 hrs then it will escalate and send the Email notification.
In above case, we are going to create two Milestones for Medium and High Priority. This will represent Time-dependent steps in Case resolution times.
3.       Now, setting up of Business Hours in your org. All the entitlements will depend on the Business hours to calculate the time, you need to setup according to your requirements.




Here I have setup for Indian Business Hours, kept 24hrs for all 7 days and If you have any Holidays then setup accordingly.

4.       Here comes, Entitlement Process setup, go to Entitlement Process and Create New Entitlement Process.


Entitlement Process Name – Specify name for entitlement.
Case enters the process - Based on Case created date – The calculations will be calculate from Created Date.
Case exits the process - Based on custom criteria - Case: Closed EQUALS True
Once the case is closed all the milestones will be completed.
                Entitlement Process Business Hours – Select required business hours which we have created in step 3.
5.     Now, in Milestone section, Click on new and setup as shown below.

Milestone NameSelect Milestone from list which you have created. Here we have only created Medium and High.
Time Trigger (Minutes)Here we have 8 Hours milestone for Medium. So in minutes it will be 8*60 =480 minutes.
Milestone Business HoursSpecify business hours.
Violation action – I have created simple email notification here, once the milestone violated after 1 min the action will be triggered.
Same as Violation action, we also have Warning and Success action.

6.     Here comes the important part, Now go to entitlement object and create record as follows.

Here the highlighted Business Hours and Entitlement Process are so important else the milestones won’t get created on Case.

7.     Now to create Milestones for Case, first case need to attach with the above Entitlement record. For this you can use Before Insert trigger to attach the Entitlement record to the Case record Or you can manually select entitlement from lookup on Case while creating Case.

8.     It’s time to create a Case, to check if this works or not. So I am creating case with Priority as ‘Medium’, After creation, the milestone should be created.


So the Medium Milestone has been created.


Note : If you want to edit criteria for Milestone setup in Entitlement process, then directly it won’t possible. You need to create new version of Entitlement and then edit the criteria.
After creating new version, don’t forget to attach it to Entitlement record, else milestones will be behave as per old entitlement process.

Friday, 12 January 2018

Lightning Out for Lightning Applications


Till now, we have “lightning component” access limited to salesforce org itself. But from Salesforce Spring ’16 Release, we can run Lightning Components Apps Anywhere.

Whether it’s a Node.js app running on Heroku, a department server inside the firewall, or even SharePoint, build your custom app with Force.com and run it wherever your users are.

 There are simple steps to use Lightning Out.

  1. Setup and preparation.
  2. Create a reference to a Lightning Components app that includes dependency information about the components you intend to use.
  3. Create components on the page to build the page’s functionality.
Follow the below steps.


1) Create the component.

ltngout.cmp

<aura:component>

         Hello! Lightning Out is here!!

</aura:component>

 2) Create the application.

LightningOut.app

<aura:application access="Global" extends="ltng:outApp" implements="ltng:allowGuestAccess " >

    <aura:dependency resource='c: ltngout />

</aura:application>

 Extending “ltng:outApp”, makes your lightning bundle publically available. Once you extend “ltng:outApp”, and you try to preview the app, it won’t run.

 3) Now create the visualforce page from where you want to run lightning app using Lightning out.

<apex:page showHeader="false" sidebar="false"    >

    <apex:includeLightning />

    <div id="lightning" />

    <script>

        $Lightning.use("c:LightningOut ", function() {

          $Lightning.createComponent("c:ltngout",

          {  },

          "lightning",

          function(cmp) {

            // do some stuff

          });

        });

    </script>

</apex:page>
c:LightningOut - Lightning application name.
c:ltngout - Lightning component name.

 Now, preview the vf page, you will see the lighting application. 
Congrats you have learned Lightning Out.
Happy Learning!!

Tuesday, 20 June 2017

Live Chat Implementation with Pre-Chat Form

In the service cloud implementation, Live agent is one of the crucial ways to give support to customer. As it makes possible for the organization to give online support for their customer, so that agents are always available to chat with the customer to give 24*7 support.

 Almost everyone knows how to implement Live Agent. But now, here you will able to learn how to implement the live agent and pre chat form implementation so that automatically Account and Case would be created when consumer submit the form. And after that, agent should chat with that consumer.

Here are the steps to implement Live agent.
Pre-requisite :
1) Create console app and Include Live Agent App in this app as “Yes”


2) You need salesforce site to implement pre chat form.
Quick search – ‘Site’ – create new site
Below I have created site for demo, I choose PreChatFormDemo(Visualforce page) as my active site home page, later below will explain the visual form page.


Implementing Live Agent

1. User should be Live Agent user – Edit user and check Live Agent User




2. First enable the Live Agent to your org.


3. Now setting up below











A. Skills :
Skills are nothing but the assigning users to Live Agent.
Go to Skills from quick search and create new


B. Setting up chat button is a main step
Here on chat button if you look closely, everything is pretty self explanatory
I have specified
Custom Agent Name – whenever agent accepts the chat request specified name will shown to consumer.
Skills – Using above created skills.
Site For Resources – Specifies site which you have created.
Online & Offline Image – specifies that which image should shown when agent is online or offline
Pre Chat Form Page – is simple vf page which specifies Live Agent API to create Account and Case.






C. Chat Button Configuration – specifies that more details about the user, auto greetings, notifications, auto declining, chat capacity and etc.


D. Deployments – will take care deploying the live agent when any requests are made through live agent, this differs your org and which api to hit.



Now, You have two code snippets generated automatically on Chat button and Deployment.



Now copy and paste both to notepad file and save it with .html extension.

All setup has been done. Here that pre chat form –

PreChatFormDemo visual for page
<apex:page showHeader="false">
<!-- This script takes the endpoint URL parameter passed from the deployment page and makes it the action for the form -->
<script type='text/javascript'>
(function() {
function handlePageLoad() {
var endpointMatcher = new RegExp("[\\?\\&]endpoint=([^&#]*)");
document.getElementById('prechatForm').setAttribute('action',
decodeURIComponent(endpointMatcher.exec(document.location.search)[1]));
} if (window.addEventListener) {
window.addEventListener('load', handlePageLoad, false);
} else { window.attachEvent('onload', handlePageLoad, false);
}})();
</script>
 
<!-- Form that gathers information from the chat visitor and sets the values to Live Agent Custom Details used later in the example -->
<form method='post' id='prechatForm' align= 'Center'>
  <table align= 'Center'>
        <tr>
            <td> Full Name: </td><td><input type='text' name='liveagent.prechat:fullName' id='Name' /><br /></td>
        </tr>
        <tr>
            <td>E-mail: </td> <td><input type='text' name='liveagent.prechat:AccountEmail' id='Email__c'  /><br /></td>
        </tr>
        <tr>
            <td>Phone: </td> <td><input type='text' name='liveagent.prechat:AccountPhone' id='Phone'  /> <br /></td>
        </tr>
      <tr>
            <td><br/>Or<br/></td>
        </tr>
<tr>
            <td><br/>Raised Case already, enter<br/></td>
        </tr>
   
      <tr>
            <td>Case Number: </td> <td><input type='text' name='liveagent.prechat:CaseN' id='CaseNumber' /> <br /></td>
        </tr>
    </table>
 
    <!-- Hidden fields used to set additional custom details -->
    <input type="hidden" name="liveagent.prechat:CaseStatus" value="New" /><br />
    <input type="hidden" name="liveagent.prechat:CasePriority" value="Low" /><br />
     
 
    <!-- Used to set the visitor's name for the agent in the Console -->
    <input type="hidden" name="liveagent.prechat.name" id="prechat_field_name" />
<!-- map: Use the data from prechat form to map it to the Salesforce record's fields -->
<input type="hidden" name="liveagent.prechat.findorcreate.map:Account" value="Name,fullName;Email__c,AccountEmail;Phone,AccountPhone" />
 
<!-- doFind, doCreate and isExactMatch example for a Contact:
    Find a contact whose Email exactly matches the value provided by the customer in the form
    If there's no match, then create a Contact record and set it's First Name, Last Name, Email, and Phone to the values provided by the customer -->
<input type="hidden" name="liveagent.prechat.findorcreate.map.doFind:Account" value="Email__c,true" />
<input type="hidden" name="liveagent.prechat.findorcreate.map.isExactMatch:Account" value="Email__c,true" />
<input type="hidden" name="liveagent.prechat.findorcreate.map.doCreate:Account" value="Name,true;Email__c,true;Phone,true;" />

<!- - If consumer has created already case and has case number with him, will put case number directly and that will search and open the case -->
<input type="hidden" name="liveagent.prechat.findorcreate.map.doFind:Case" value="CaseNumber,true" />
<input type="hidden" name="liveagent.prechat.findorcreate.map.isExactMatch:Case" value="CaseNumber,true" /> 
<!—Mapping case fields with specified chat fields. We can map standard as well as custom fields. -->
<input type="hidden" name="liveagent.prechat.findorcreate.map:Case" value="CaseNumber,CaseN;Subject,CaseSubject;Status,CaseStatus;Priority,CasePriority;" />


<!-- doCreate example for a Case: create a case to attach to the chat, set the Case Subject to the value provided by the customer and set the case's Status and Origin fields -->
<input type="hidden" name="liveagent.prechat.findorcreate.map.doCreate:Case" value="CaseNumber,true;Subject,true;Status,true;Origin,true;Priority,true" />
<!-- linkToEntity: Set the record Contact record, found/created above, as the Contact on the Case that's created -->
<input type="hidden" name="liveagent.prechat.findorcreate.linkToEntity:Account" value="Case,AccountId" /> 
<!-- showOnCreate: Open the Account and Case records as sub-tabs to the chat for the agent in the Console -->
<input type="hidden" name="liveagent.prechat.findorcreate.showOnCreate:Account" value="true" />
<input type="hidden" name="liveagent.prechat.findorcreate.showOnCreate:Case" value="true" /> 
<!-- saveToTranscript: Associates the records found / created, i.e. Account and Case, to the Live Chat Transcript record. -->
<input type="hidden" name="liveagent.prechat.findorcreate.saveToTranscript:Account" value="AccountId" />
<input type="hidden" name="liveagent.prechat.findorcreate.saveToTranscript:Case" value="CaseId" />
<!-- displayToAgent: Hides the case record type from the agent -->
<input type="hidden" name="liveagent.prechat.findorcreate.displayToAgent:CaseRecordType" value="false" />
<!-- searchKnowledge: Searches knowledge article based on the text, this assumes that Knowledge is setup -->
<input type="hidden" name="liveagent.prechat.knowledgeSearch:CaseSubject" value="true" />
 
<!-- Link the Account to the Case -->
<input type= "hidden" name="liveagent.prechat.findorcreate.linkToEntity:Account" value="Case,AccountId" />
 
<input type='submit' value='Click to Chat' id='prechat_submit' />
<style type="text/css">
p {font-weight: bolder }
</style>
</form>
</apex:page>

Here is the demo :