Salesforce Report, KPI Dashboard and Chart.JS




In previous post I showed how we can use Plotly in Salesofrce Lightning component.
Another powerful library for graphs is Chart.Js.

I created recently package that allow to setup KPI and view them in custom dashboard using either Plotly or Chart.JS.
The latest question I was facing is how I can include Salesforce standard reports in the KPI dashboard. 
We do know that Salesforce reports have some limitations, but still they are commonly and if there are logic that can be achieve with the standard tool we should use it.

The good thing is that Salesforce have almost

As a demo, I implemented the same example from the
The Lightning component updated to use the chart.js static resource



1
2
3
4
5
6
<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" access="global" controller="PlotlyDemoController">
    <ltng:require scripts="/resource/chartJS/Chart.bundle.js" afterScriptsLoaded="{!c.scriptsLoaded}"/>
    <div style="width:600px;height:400px;">
     <canvas id="myDiv" ></canvas>
    </div>
</aura:component>

The Lightning JS controller was updated to match the chart js input



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
({
    scriptsLoaded : function(component, event, helper) {
        var getAccountDataAction = component.get("c.getAccountData");
        
        getAccountDataAction.setParams({
            'accountId' : component.get("v.recordId")
        });
        
        getAccountDataAction.setCallback(this, function(response){
            
            var resData = JSON.parse(response.getReturnValue());
            
            
            var ctx = document.getElementById('myDiv').getContext('2d');
            var myChart = new Chart(ctx, {
                type: 'bar',
                data: {
                    labels: resData.labels,
                    datasets: [{
                        data: resData.values,
                        backgroundColor: '#1f77b4'
                    }]
                },
                options: {
                    responsive:true,
                    maintainAspectRatio: false
                }
            });
        });
        
        $A.enqueueAction(getAccountDataAction);
    }
})

And last the the apex controller was changed to read its input from standard report (+building Salesforce standard report that show the opportunity grouped by  type).



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public with sharing class PlotlyDemoController{  
    
    @AuraEnabled
    public static String getAccountData(String accountId){
    
        map<String, object> m_retData = new map<String, object>();
        
        map<String, decimal> m_oppType_Amt = new map<String, decimal>();
        
        Report rep = [select Id from Report where DeveloperName = 'Account_Opp_Report1'];
        
        //Run the report
        Reports.ReportResults repResult = Reports.ReportManager.runReport(rep.Id, false);
        
        //Read result and grouping property
        list<Reports.GroupingValue> l_groupingValue = repResult.getGroupingsDown().getGroupings();
        map<String,Reports.ReportFact> m_reportFact = repResult.getFactMap();

        map<String, object> m_cell_sumValue = new map<String, object>();
        
        //Store each per each uniuque cell its summary value
        for(String keyFact : m_reportFact.keyset()){
            list<Reports.SummaryValue> l_summryValues = m_reportFact.get(keyFact).getAggregates();
            String keyToUse = m_reportFact.get(keyFact).getKey().replace('!T', '');
    
            m_cell_sumValue.put(keyToUse, l_summryValues.get(0).getValue());    //*Might have several summaries. In this assume the first summary if the sum amount that we need
        }
        
        //Per each grouping label store it in the final result
        for(Reports.GroupingValue grupingItem : l_groupingValue){
            m_oppType_Amt.put(grupingItem.getLabel(), decimal.valueOf(String.valueOf(m_cell_sumValue.get(grupingItem.getKey()))));
        }        
        
        m_retData.put('labels', m_oppType_Amt.keySet());
        m_retData.put('values', m_oppType_Amt.values());
        
        return JSON.serialize(m_retData);
    }

}


Finally, this is a quick demo how I used all those items together in the KPI dashboard app.







Using Plotly in Lightning Component

Plotly has Javascript open source library that can be used to easily create powerful graphs.
But can we use it in Salesforce Lightning Component? We can. 
Under some consideration.

First, in Lightning we cannot reference external javascript files, therefore the plotly library must be downloaded and saved as static resource. 
Second, Lightning component with API higher than 39 uses locker service which make the component more secure but also more isolate, therefore you will need to use component with API version 39 (or less).

With those settings it is possible and quite easy to use Plotly.
Here is very simple example of Lightning component embedded in account Lightning page that display the total opportunities amount grouped by opportunities type.



1.Apex controller - use to collect opportunities data


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public with sharing class PlotlyDemoController{


    @AuraEnabled
    public static String getAccountData(String accountId){
    
        map<String, object> m_retData = new map<String, object>();
        
        
        map<String, decimal> m_oppType_Amt = new map<String, decimal>();
        
        for(Opportunity opp : [select Id,Type,Amount from Opportunity where AccountId = :accountId]){
            if(! m_oppType_Amt.containsKey(opp.Type)){
                m_oppType_Amt.put(opp.Type, 0);
            }
            
            m_oppType_Amt.put(opp.Type, opp.Amount + m_oppType_Amt.get(opp.Type));
        }
        
        m_retData.put('labels', m_oppType_Amt.keySet());
        m_retData.put('values', m_oppType_Amt.values());
        
        return JSON.serialize(m_retData);
    }

}


2.Lightning component


1
2
3
4
<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" access="global" controller="PlotlyDemoController">
    <ltng:require scripts="{!$Resource.plotlyCode}" afterScriptsLoaded="{!c.scriptsLoaded}"/>
    <div id="myDiv" style="width:600px;height:400px;"></div>
</aura:component>



3.Lighting Controller


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
({
    scriptsLoaded : function(component, event, helper) {
        var getAccountDataAction = component.get("c.getAccountData");
        
        getAccountDataAction.setParams({
            'accountId' : component.get("v.recordId")
        });
        
        getAccountDataAction.setCallback(this, function(response){
            
            var resData = JSON.parse(response.getReturnValue());
            
            var data = [
              {
                x: resData.labels,
                y: resData.values,
                type: 'bar'
              }
            ];
            
            Plotly.newPlot('myDiv', data);
        });
        
        $A.enqueueAction(getAccountDataAction);
    }
})



😏

KPI Dashboard with Dynamic Parameters

Recently I introduce app for manage business activities in Salesforce
One of the module in the application is KPI Dashboards. 
It provide the ability to set up KPIs for any data type in the organization, build dashboards based on those KPIs, schedule it and distribute the results to users. 
Some questions that I was facing is if/how it is possible to run dashboard with dynamic parameters. For example - build dashboard with account parameter and run it every time for different account. 
Indeed such option was missing, so I added it.

For this example I'm using 4 KPIs related to opportunity and opportunity product, each result is split to quarters.

  1. Counting closed opportunities
  2. Sum the opportunities amount
  3. Calculate average days it took to close
  4. Counting the products solds

In the app I created those 4 KPI Setup.
Notice that each have filter with Account Id equal <empty value>, as I want to send it as parameter later.







Next, I created dashboard with those 4 KPIs, added audience and set my user as recipient.




Finally, clicked on button Schedule to run the calculations and here I added the new checkbox - Use Dynamic Parameters. Checking it will show list of available filters according to the KPIs Setup filters.


I can set different values for each KPI, or use the same value for all (that are using the same field filter). This is my case - I want to filter all the KPIs according to the same account.






After few minutes the process completed and I got email with link to view the dashboard result.









Test Class Builder App

I created simple and free app that can be useful for Salesforce development. It mainly used to simplify the process of writing test classes. 
It does not generate test classes automatically as I saw several other apps attend to do, but instead the app provide simple UI for generate test data and the test processes and finally generate 2 output files:
1.Static resource file that contains the test meta data.
2.Apex test class that run the test.

Using the app require permission: Customize Application.
In addition you should have at least Salesforce Developer basic knowledge.


Following are the main steps for creating new apex test class with the app:

1.Go to tab Test Class Builder
2.Either provide name for new test or select test file that was created before.



3.In the Test Data section you can create data that will be created before the test (before Test.StartTest()). Can either create new data using the  icon or import data from the current environment using the  icon.
  • Can add connections between the records by clicking the  icon next to the record and set the relevant lookup field.
  • Known limitation: rich text fields being displayed as regular text area field.

4.In the Test Parameters section you can define parameters that will be used during the test. 
For example, if you added few accounts in the test data and you need to test function that receive list of accounts you can create list parameter with those accounts. 

If you need to use specific field from the test record as parameter you can also create parameter for that purpose



5.Create Test Process by clicking the icon at top. 

In each test process you can have 3 steps:
5.1 Data manipulation. Add by the icon.
Usually used to test triggers functionality. For example if you have logic that invoked from trigger when opportunity is closed, you should update the opportunity to close.
  • Click the icon to set fields to update.


5.2 Methods invocation. Add by theicon.
Used to call directly to apex function inside classes.
First type the class you want to test and select it, the page will show all the functions in this class. Select the relevant functions and click button Add Selected.

  • For this demo I created simple apex class with 2 functions: first receive list of account records and print their names, second receive account Id parameter and close its opportunities
  • If the function receive parameters you can set them by selecting parameters that were defined under Test Parameters.


  • Use the  icon to reorder the function order. The order in the page is the order that the functions will be executed.

5.3 Assert. Add by the icon.
Can be used to verify your test complete as expected.
Select the Assert Type (for now only 1 option exists Field Value) and click the icon to choose the record, field, expected value and error message to show if the assert failed.


6.Click the icon to generate your test files.
You can receive email with the 2 output files, or if it is sandbox you can directly deploy the files to this org.



If you used the email, process the instruction in the email - 1 file should be added as static resource, second as new apex class.
If you deployed the files, you should find your apex test class under the name {input name at start}Test, in our demo it will be demo01Test.

  • If using in sandbox and you want to use the deploy option you should first add remote site setting with your current org. If you not sure what is the URL, just try the deployment and you will receive appropriate message indicating which URL should be added.
  • You can modify the apex classes that was generated by the app. For example if you have complex logic that is hard to implement with the app, you can use the app only to create the test data and then write the test logic by yourself in the output file.


Finally, run the test class and after verifying the coverage deploy to production both the static resource and the apex text class.

Installation link

Sample video:

😏


Retire of Permission on Profiles

If you are working as a Salesforce admin/developer you've probably heard somewhere that Salesforce is planning to make a significant cha...