Pages

Monday, May 14, 2018

Einstein Analytics: The quest for Binding in Dashboard

This blog is written in Summer '18 release, so it may become invalid when Salesforce makes "binding" become more user-friendly in the future.

Until Summer '18 release, if you would like to implement binding in the dashboard, you need to manually edit the dashboard JSON.

Use case: you would like to give the user flexibility to change chart grouping, for example group by Region, or by Country, or by Status. Another use case, the user would like to have the flexibility to change chart type without the need to edit the dashboard or would like to implement both in the same dashboard, this makes sense when you change grouping, the chart type probably needs to change for better visualization.

This blog will not share on how to create binding or static with toggle, you can watch the awesome Peter Lyon's videos Binding Basic, and Rikke Hovgaard's blog the power of static steps.

A. Binding for Chart
In this sample, I have one chart and 3 toggles:
- Chart
   Step Id: step_1
- Field Names for grouping
   Step Id: static_1
- Chart Type to change the visualization
   Step Id: static_2
- Order by ascending or descending
   Step Id: sort_1



This is how the dashboard looks


To make the binding work, I am going to edit the JSON with Ctrl-E.

Binding to change Grouping

1. Change "groups" query under "step_1"
change from
"groups": ["field_name"]
change to
"groups": ["{{cell(static_1.selection, 0, \"value\").asString()}}"]

When previewing the dashboard, if the selected field in toggle different from the field use in the initial value for grouping, the chart will error:



2. Change "columnMap" in "widgets"
"columnMap": null OR delete the whole columnMap


Binding to change Chart type

1. Change "columnMap" in "widgets" (if you have not done it)
"columnMap": null OR delete the whole columnMap

2. Change "visualizationType" under "parameters" in "widgets" 
change from
"visualizationType": "hbar"
change to
"visualizationType": "{{cell(static_2.selection, 0, \"value\").asString()}}"



Binding for Date

In this scenario, we would like to give users the flexibility to view a timeline chart by week and by day. Sure we need binding for this.

Here is the static step:
                    {
                        "display": "Day",
                        "value": [
                            "TIMESTAMP_Year",
                            "TIMESTAMP_Month",
                            "TIMESTAMP_Day"
                        ]
                    },
                    {
                        "display": "Week",
                        "value": [
                            "TIMESTAMP_Year",
                            "TIMESTAMP_Week"
                        ]
                    }

and change step query for "groups" 
from: 
"groups": [
                        [
                            "TIMESTAMP_Year",
                            "TIMESTAMP_Month",
                            "TIMESTAMP_Day"
                        ]
                    ]
to:
"groups": [
   "{{cell(staticTime_1.selection, 0, \"value\").asObject()}}"
          ]
* staticTime_1 is the step name


Binding for Order

1ST OPTION  - put "ascending" in static
1. Edit the static step under "sort_1"
change from 
"values": [
           {"display": "Asc","value": "true"},
           {"display": "Desc","value": "false"}
          ]
change to 
"values": [
           {
             "display": "Asc",
             "value": 
               [
                 -1,
                 {"ascending": true}
               ]
           },
           {
             "display": "Desc",
             "value": 
               [
                 -1,
                 {"ascending": false}
               ]
           }
          ]

2. Add "order" query under "step_1"
"query": 
{ 
  "measures": [["count","*"]],
  "groups":[
             "{{cell(static_1.selection, 0, \"value\").asString()}}"
           ],
  "order": [
             "{{column(sort_1.selection, [\"value\"]).asObject()}}"
           ]
}


2ND OPTION - put "ascending" in query order
1. Edit the static step under "sort_1"
change from 
"values": [
           {"display": "Asc","value": "true"},
           {"display": "Desc","value": "false"}
          ]
change to 
"values": [
           {"display": "Asc","value": true},
           {"display": "Desc","value": false}
          ]

2. Add "order" query under "step_1"
"query": 
{ 
  "measures": [["count","*"]],
  "groups": [
             "{{cell(static_1.selection, 0, \"value\").asString()}}"
            ],
  "order": [
            [
              "count",
              {
                "ascending": "{{cell(sort_1.selection, 0, \"value\").asObject()}}"
              }
            ]
           ]
}



B. Binding for table
We also can use a toggle to sort a table widget.

Here is a sample with JSON snippet from steps:
  "query": {  
           "values": [  
             "Region__c",
             "Skill__c",
             "Name",
             "Id",
             "LastModifiedDate"  
           ],  
           "order": [  
             [  
               "{{ cell(static_2.selection, 0, \"value\").asString() }}"               
               {  
                  "ascending": "{{ cell(sort_2.selection, 0, \"value\").asObject }}"
               }  
             ]  
           ]  
         }  

This is the JSON step for the fields selection:
      "static_2": {  
         "broadcastFacet": true,  
         "label": "static 2",  
         "selectMode": "singlerequired",  
         "type": "staticflex",  
         "values": [             {  
             "display": "Region",  
             "value": "Region__c"  
           },  
           {  
             "display": "Status",  
             "value": "Status__c"  
           }  
         ]  
       }  

This is the JSON step for the order of the field:
     "sort_2": {  
         "type": "staticflex",  
         "broadcastFacet": true,  
         "selectMode": "singlerequired",  
         "label": "sort 2",  
         "values": [  
           {  
             "display": "Ascending",  
             "value": true  
           },  
           {  
             "display": "Descending",  
             "value": false  
           }  
         ]  
       }  
notice that true and false are not in the double quote ""

Table result:


Here is the full JSON for the dashboard.


Side note: old-style binding
"{{ value(selection(static_2)) }}" 
this is equal to
"{{ cell(static_2.selection, 0, \"value\").asObject() }}"



Reference:

Sunday, May 6, 2018

Einstein Analytics: License Assignment

So your company purchase X licenses of Einstein Analytics, perhaps with Event Monitoring too. To check the licenses you have acquired, go to Company Information in setup menu.

In Company Information page, scroll down to Permission Set Licenses section.


From above screenshot:
  • Analytics Platform (yellow highlight): this is the license for Einstein Analytic, I have a total of 2 licenses, but 1 used, so remain 1 license.
  • Event Monitoring Analytics Apps (green highlight): this is the license for Event Monitoring, I have a total of 2 licenses, but 1 used, so remain 1 license.

To assign licenses to the user, go to the user detail, you will notice 
  1. Permission Set Assignments (PS)
  2. Permission Set Assignments: Activation Required
  3. Permission Set License Assignments (PSL)
When assigning licenses, you just need to pay attention to PS (1) and PSL (3) only. I borrow a good sample from Trailhead Assign Permissions:
  •  A PSL is like a passport. It grants you the right to travel, but you can’t visit the great land of Analytics without the right visa. 
  • A PS is like a visa. You can get a 3-day tourist visa, a work visa, or a student visa. Each visa type lets you do certain things.
  • Just like a traveler needs both a passport and a visa, your Analytics users need at least one PSL and a PS.
Back to license count, once you assign a user with a PSL, it counts as a license is used. Imagine that your country has a right to issue 100 passport, once a user Mr. X get a passport, as a country, you only can issue another 99 passports for your citizen, no matter if Mr. X apply any visa to USA, UK, or etc. But, for Mr. X to travel to the USA, he needs to obtain a visa, which is Permission Set.

In the real world, user ideally needs to get a passport first (PSL), before applying for a visa (PS). But, Salesforce makes our life as an admin easier, we can grant PS (visa) Einstein Analytics Platform User or Einstein Analytics Platform Admin directly to the user, at the same time, a passport will be issued too (PSL) Analytics Platform.


Now if you check back your license usage, it will mention 2 Analytics Platform has been used. 

Because PSL is required for PS Einstein Analytics Platform User or Einstein Analytics Platform Admin, you cannot delete the license PSL Analytics Platform (passport) before deleting the PS Einstein Analytics Platform User or Einstein Analytics Platform Admin (visa). 


But, when you delete PS from the user detail, the PSL will stay, and will still count to your license usage.


Query License Usage
To understand who is assigned with the PSL, you can do a simple query:
SELECT Id, PermissionSetLicense.MasterLabel, PermissionSetLicense.TotalLicenses, PermissionSetLicense.UsedLicenses, Assignee.Name FROM PermissionSetLicenseAssign



PS: Einstein Analytics Platform User or Einstein Analytics Platform Admin
Einstein Analytics Platform User is designed to be assigned to users need to explore dataset with lenses and build dashboards.
Einstein Analytics Platform Admin is designed to be assigned to admin, they will be able to create and customize Apps, Dashboards, Datasets, Dataflows, and Recipes, including Monitor from Data Manager. These users will be able to view all apps in Einstein Analytics, except items stored in My Private App.

Einstein Analytics Platform User Einstein Analytics Platform Admin
Access EA
  • Analytics tab
  • Analytics Studio app
  • Analytics tab
  • Analytics Studio app
Allow creating
  • Dashboard
  • App
  • Dashboard
  • Dataset
View all Apps/data No Yes
Explore Lens Yes Yes
Access Data Manager No Yes (incl. Dataflow & Recipe)



Reference:

Saturday, May 5, 2018

Salesforce: Picklist Default Value

Since Summer '17 release, Salesforce supports Default Value at the field level, this means we can define different default value based on the user, example: when front-end support creates a new case, Priority default value "high", while all other users will have the Priority default value to "low", although they can change it manually.

But, the field with picklist type will have its own default value, how this works with the default value introduced in Summer '17 release? In summary, it can be up to 3 places for a picklist field to have a default value defined.

Below is a simple logic on how this works, I would agree it would be easier to read this in a flowchart.

If Default Value in General Options (field level) is defined
    if Record Type* for the object is defined
        if Formula General Option resolve** to an active item & available in Record Type
            --> use resolve Value from General Options
        else if there is Default Value selected in Record Type
            --> use selected Default Value in Record Type
        else
            --> no default value
        endif
    else
        if Formula General Option resolve** to an active item
            --> use resolve Value from General Options
        else if there is Default Value selected in Field Picklist
            --> use selected Default Value in Field Picklist
        else
            --> no default value
        endif
    endif
else
    if Record Type for the object is defined
        if there is Default Value selected in Record Type
            --> use selected Default Value in Record Type
        else if there is Default Value selected in Field Picklist
            --> use selected Default Value in Field Picklist
        else
            --> no default value
        endif
    else
        if there is Default Value selected in Field Picklist
            --> use selected Default Value in Field Picklist
        else
            --> no default value
        endif
endif

* even there is only 1 record type defined and active
** the formula resolved is case-sensitive with the item API name

Note: from the above logic, the default value from the field level is processed first, if no or fail fit to API name, check if any record type and the default value, and the last would be the default value in the picklist. 
The default value in record type is treated at the same level as the default value from the picklist, therefore, for an object with record type, if there is no default value defined, the system will not check further if any default value selected in the picklist field.


Here a few samples:
there is no default value defined at the field level, all user will get the default value from the selected item


The default value in the field resolved as High1, while the picklist API name is High, the default value from the field level will not apply - it is case-sensitive too.


Reference:




Friday, May 4, 2018

Einstein Analytics: Convert DateTime field to Date field or Text field

This would be a simple tip for you that start using Einstein Analytics.

Use case: to convert DateTime field (copy from Salesforce) to Date field, or Text field in Einstein Analytics.

In this blog, I'll add new fields in Dataflow using computeExpression. We will use a DateTime field with API name: Submitted_Date_Time__c

Text
formula: substr(Submitted_Date_Time__c,1,10)
This formula will take the first 10 characters.
Original: 2017-05-03T09:43:28.000Z --> 2017-05-03

Date
formula: toDate(Submitted_Date_Time__c_sec_epoch)
Remember to enter "Date Format" (you need to scroll down to find it), otherwise, you can't upload the Dataflow.

Side note: toDate() is case-sensitive.



Reference:


Thursday, April 26, 2018

Salesforce Email Logs

Few details about Salesforce email logs:
  • Email logs should be available within 30 minutes of your request.
  • Email logs are available for messages sent within the past 30 days before your request. 
  • Each email log can span a maximum of 7 days. To see email log data for a duration longer than seven days, create multiple requests. 
  • Email logs include emails sent through email actions, list email, and mass email, as long as the emails are sent through Salesforce.

In this blog, I'll not share on how to request email log, you can refer to this documentation Request an Email Log, and check this documentation Email Log Reference to understand the format and field values of email log files. I'll share columns need to put attention when you are tracing email send out from Salesforce:

Date Time
The date and time here are always in GMT, so make sure to convert into your timezone if you are checking for specific email at certain times. But, when you request for email logs, enter the time in your local timezone.

Mail Event
D - Delivery: the email was successfully sent to the recipients.
R - Reception: the email was successfully received by Salesforce email server.

Recipient
The email address of the person to whom the email is sent.

Sender
The email address of the person who sent the email, but in many cases, this will not show the exact email address, but mailbox name before @ will be there, example: jjohan=mydomain.com__1l6m60naoh7y3pvy.7zbt7fbfwio41y1c@castgfakbl6ndktj.e2g41.2a-z8jeae.cs47.bnc.sandbox.salesforce.com

Sample:
  • There are 3 color highlights in above screenshot, this is for 3 difference emails.
  • Email received by Salesforce email server (R), then send out in a few seconds (D).
  • The yellow highlight means, there are 3 recipients in that one email, row 7 correspondence to row 3, row 6 correspondence to row 4, and row 5 correspondence to row 2. Each pair has the same Internal Message ID.


When you open email sent in Gmail, you will notice email sender contain a portion of the sender in email logs (see above and below screenshots in pink).


Let's see more columns:

Remote Host
This is IP address of the email server that received the email sent.

Bytes Transferred
The size of the email in "byte", not KB or MB.

Salesforce.com User
The Salesforce ID of the user who sent the email.

Message ID Header
If you are using Gmail, you can check this by click "Show Original"



The screenshot below shows the message header in Gmail, see the highlights in green.



Reference:


Wednesday, April 25, 2018

Salesforce: Opportunity Team Report

Two years ago, I wrote a blog about reporting for the Account Team, this blog would be for the Opportunity Team report. When you use the Opportunity report type, you can filter the result based on the owner, with options:



Let's see the difference between each option. In this sample, I'll login as "Maria Ann" and she has 2 subordinates: "Song Lee" and "Free Man".

My opportunities
All opportunities are owned by me, period!



My team-selling opportunities
All opportunities where I am listed in the Opportunity Team, no matter if I am the opportunity owner or not.



My team-selling and my opportunities 
This is the combination of My opportunities and My team-selling opportunities, although it doesn't mean the opportunity will be listed twice if it fit into both criteria, so you can't just sum the number of opportunity from My opportunities with My team-selling opportunities.



My team's opportunities
All opportunities owned by me and all opportunities owned by users below my role hierarchy, including the indirect reports.



My team's team-selling and their opportunities
All opportunities where I or any users below my role hierarchy were listed as in the Opportunity Team, no matter who owned the opportunity. This is similar to My team-selling opportunities but extended to my subordinates.



My territories' opportunities
Only available when Territory Management is enabled.



All Opportunities
All opportunities that you have visibility.




Filter by Role

Only for Opportunity report type, you can add a filter by Role, where this will filter only opportunities owned by users in the selected role, including users in the subordinates in the role hierarchy.

Additionally, you can narrow by person, by selecting a user name in the selected role hierarchy, this will only be impactful when there is more than 1 user in the same role hierarchy, while all the subordinates are still the same.

Page-level ad