Pages

Tuesday, December 31, 2019

Einstein Prediction Builder: Getting Started

Einstein Prediction Builder allows you to easily build predictions across any Salesforce standard or custom object with clicks, no code! It predicts a field based on a set of historical data.

If you have Einstein Predictions or Einstein Analytics Plus license, then Einstein Prediction Builder is available for you to use, source Einstein Analytics Pricing (as of 31 Dec 2019).



If you org. have Einstein Prediction Builder license, you should see Einstein Prediction Builder in the setup menu.



There is a few blogs have shared on Einstein Prediction Builder use cases and how to configure it:
Einstein Prediction Builder. The Artificial Intelligence by Salesforce
Einstein prediction builder to predict
Einstein Prediction Builder

Einstein Prediction Builder comes with a wizard-style configuration, you need to select an object, a field for prediction, a set of records as "example", and field to store the prediction result.

Currently, it only supports to predict a checkbox field, while the result would be in number between 0 to 100. In summary, here are the steps:

1. Object
Select a Salesforce object you would like to use for prediction.

2. Segment
Do you want to use the whole data of the object for prediction and learning? Select:
- No segment, to use all data, or
- Yes, enter filter of fields to be included
Make sure you have a minimum of 400 records (after filter if you segment the data).

3. Field to Predict 
This is a very important field, Einstein will do the prediction based on historical value of this field, and it needs to be a checkbox or formula that returns checkbox.

In this blog, I have a picklist field called "Status" with value: Sent, No Show, and Attended. No Show and Attended is historical data, while Sent is new records, and Einstein will help us to predict the attendance for all Sent records.

Because it needs to be a checkbox field, let us create a formula field that returns checkbox called "Attend", ISPICKVAL(Status__c,"Attended"), this field will return True if the Status field is Attended.



4. Define a set of data as examples (for learning)
As mentioned that Einstein needs historical data to predict, we need to define historical data for learning. Einstein will not predict records set as examples, but use it to learn the value of "field to predict".

The more samples data (historical) is a better prediction, but make sure to have a minimum of 400 example records, with both Yes and No records should have a minimum of 100 records.


From the above sample screenshot:
- 407 is the historical data for learning, which are records with Status does not equal to Sent
- 188 of them is Yes <-- this means Attend value is True
- 219 of them is No <-- this means Attend value is False
- 3399 records with Status = Sent, this needs to be predicted for the attendance



The below image explains dataset scope:

- Green: whole data in an object
- Yellow: data filtered after segment, data outside yellow will not be used for prediction and learning
- Dark Yellow: historical data set for learning (Status = No Show and Attended)
- Light Yellow: new data set for prediction (Status = Sent)


5. Set a field to store prediction values
Once the predictions enabled, the values will be range from 0 to 100.


Depends on your data size, Einstein may take up to 24 hours to build the prediction, then you can review the scorecard. Once you enable the prediction, Einstein will start to populate the prediction field with a score, including new records.



Reference:



Friday, December 27, 2019

Einstein Analytics: Working with Null Dimension with Default Value

A dimension field in Einstein Analytics is equal to a string field. However, by default Einstein Analytics does not show NULL value in grouping or sorting.

From the sample report below, we have 1004 accounts, and 5 of them do not have Account Source.



With dataflow, we get all the Account from Salesforce to Einstein Analytics, this clearly shows the number of Accounts in the dataset is 1004.



Then, let us group it by Account Source



Notice from the above screenshot, Accounts with null Account Source do not show at all, the same if we change it to Compare Table.



In Values Table, Account with null Account Source still appears.


But, if we sort by Account Source column, the rows will no longer show.


So, instead of keeping it null, we can transform the value as N/A in Einstein Analytics. We can do this from sfdcDigest note in Dataflow.
Open the sfdcDigest node for Account and click the pencil icon next to Account Source.



In the Default Value, populate N/A, and click the Save button.



Notice the pencil icon next to Account Source is changed, this tells us that something has been updated for that field, click Save button again to close sfdcDigest window.



If you check the dataflow JSON, defaultValue is added

"sfdcDigest_Account": {
    "action": "sfdcDigest",
    "parameters": {
      "fields": [
        {
          "name": "Id"
        },
        {
          "name": "Name"
        },
        {
          "name": "AccountSource",
          "defaultValue": "N/A"
        }
      ],
      "object": "Account"
    }
}


Now, update the dataflow and run it again, then group the lens with Account Source, notice N/A added for all null values Account Source.




Wednesday, December 25, 2019

Einstein Analytics: Use Image as Link

In Einstein Analytics, link by default only offers text. But instead of only text, can we use an image as a clickable link?
Yes, the trick is to use a container then add a link widget on the container, then make the link widget background as transparent and the text as transparent too.

Container
- Add a small container widget with the size similar to image size for the dashboard
- From widget properties, under "Background Image" section, click Upload Image then select an image
- Set the Image Scale and Image Alignment properly



Link
- Add link widget in the container widget added
- Resize the link widget size, to cover the whole container widget
- Set "Link To" to a dashboard or lens or page or URL as needed
- In the "Text Style" section, change the color as transparent, by click Custom and drag the slider to the far left


- Same for "Widget Style", set the Background Color to transparent too, make sure it is "rgba(x,y,z,0)" with the last number is 0.



Tuesday, December 24, 2019

Einstein Data Insights: Analyze Reports Data

Einstein Data Insights requires the Einstein Analytics Plus license. Einstein Data Insights analyze your report data and provide you with insights, complete with charts and explanations.

Setup
1. Enable Einstein Data Insights
From Setup, enter Einstein Data Insights in the Quick Find box. Under Einstein Data Insights, click Getting Started.

2. Grant User Access
Users need to have the standard permission set "Einstein Analytics Plus User" or "Einstein Analytics Plus Admin", or custom permission set include the "Can Run Einstein Data Insights" permission.


Analyze Report Data
1. The report must be in tabular or summary report type, matrix and joined report is not supported. If you use the summary report, make sure the "Detail Rows" are shown.
2. Report column: 2 - 50 columns.
3. Report row: 50 - 500,000 rows, even report only display 2,000 rows, einstein will analyze the whole data, not just 2,000 records shown.


Insights button
You will not see the Insights button or the button will be disabled in the report if any of the above criteria are not fulfilled, from the license, user permission, report format, number of rows and columns.



Start Analysis
You can create an analysis by:
- record occurrence
- a number field in the report

Then tell the system if you would like to Maximize or Minimize before analyzing the report, in my case, I would like to maximize Won Opportunities. You will get the analysis almost immediately, depends on the report rows result and number of columns.



Click "learn more" below each box to get the detail of the analysis. You also can click the title to get a bigger view of the analysis.




ReferenceAnalyze Reports with Einstein Data Insights




Thursday, November 28, 2019

Einstein Analytics: Binding serialization functions asString() or asObject()

We discussed the quest for here and the syntax here, and you may see we use asString() and asObject() in the samples, as per Einstein Analytics documentation:

asString() Function
Serializes a scalar, one-dimensional array, or two-dimensional array as a string. Escapes double quotes in strings.

asObject() Function
Passes data through with no serialization. Returns data as an object (an array of strings).

But, let us see in samples:

Selection binding with a value
Static Step
     "static_Owner_Manager": {
                "broadcastFacet": true,
                "label": "static_Owner_Manager",
                "selectMode": "singlerequired",
                "start": {
                    "display": [
                        "Owner"
                    ]
                },
                "type": "staticflex",
                "values": [
                    {
                        "display": "Owner",
                        "value": "Owner.Name",
                        "text": "Count Owner Name"
                    },
                    {
                        "display": "Manager",
                        "value": "Owner.Manager.Name",
                        "text": "Count Owner Manager Name"
                    }
                ]
            }

Binding
"groups": [
"{{cell(static_Owner_Manager.selection, 0, \"value\").asString()}}"
]

However using asObject() will Not throw error too
"groups": [
"{{cell(static_Owner_Manager.selection, 0, \"value\").asObject()}}"
]


"measures": [
                [
                    "sum",
                    "{{cell(static_Owner_Manager.selection, 0, \"value\").asString()}}"
                ]
]

The same, using asObject() will Not throw error too
"measures": [
                [
                    "sum",
                    "{{cell(static_Owner_Manager.selection, 0, \"value\").asObject()}}"
                ]
]


Selection binding with multiple values
Sample 1: selection binding in groups
Static Step
"staticTime_DATE": {
                "broadcastFacet": true,
                "label": "staticTime_DATE",
                "selectMode": "singlerequired",
                "start": {
                    "display": [
                        "Month"
                    ]
                },
                "type": "staticflex",
                "values": [
                    {
                        "display": "Month",
                        "value": [
                            "DATE_Year",
                            "DATE_Month"
                        ]
                    },
                    {
                        "display": "Week",
                        "value": [
                            "DATE_Year",
                            "DATE_Week"
                        ]
                    }
                ]
            }

Binding
"groups": [
"{{ cell(staticTime_DATE.selection, 0, \"value\").asObject() }}",
"field_name__c"
]

Sample 2: selection binding in measures
Static Step
"static_Amount_Count": {
                "broadcastFacet": true,
                "label": "static_Amount_Count",
                "selectMode": "singlerequired",
                "start": {
                    "display": [
                        "Amount"
                    ]
                },
                "type": "staticflex",
                "values": [
                    {
                        "display": "Amount",
                        "value": [
                            "sum",
                            "Amount"
                        ]
                    },
                    {
                        "display": "Count",
                        "value": [
                            "count",
                            "*"
                        ]
                    },
{
                        "display": "Unique",
                        "value": [
                            "unique",
                            "Id"
                        ]
                    }
                ]
            }

Binding
"measures": [
"{{ cell(static_Amount_Count.selection, 0, \"value\").asObject() }}"
                   ]


In short:
- use asString() when you only bind a string value
- use asObject() when you only bind multiple values
- use asObject() when you bind not a string value, such as true / false


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

Side note: column binding
"{{ cell(static_Order_By.selection, 0, \"value\").asString() }}"
equal to
"{{ column(static_Order_By.selection, [\"value\"]).asObject() }}"




Reference:


Monday, October 28, 2019

Einstein Analytics: Auto-filter dashboard based on User

You can set initial selections based on the following Salesforce user tokens: user.id, user.name, user.rolename, and user.roleid. At runtime, Analytics retrieves the values of these tokens from Salesforce environment variables—these values don’t come from datasets.

Sample-1:
"Owner_Name_1": {
                "datasets": [
                    {
                        "id": "0FbB00000000pNNKAY",
                        "label": "Opportunities",
                        "name": "opportunity",
                        "url": "/services/data/v38.0/wave/datasets/0FbB00000000pNNKAY"
                    }
                ],
                "isFacet": true,
                "isGlobal": false,
                "query": {
                    "measures": [
                        [
                            "count",
                            "*"
                        ]
                    ],
                    "groups": [
                        "Owner.Name"
                    ]
                },
                "selectMode": "multi",
                "start": [
                    "!{User.Name}"
                ],
                "type": "aggregateflex",
                "useGlobal": false,
                "visualizationParameters": {
                    "options": {}
                }
            },

use it in another step:
"Owner_Username_3": {
                "datasets": [
                    {
                        "id": "0FbB00000000pNNKAY",
                        "label": "Opportunities",
                        "name": "opportunity",
                        "url": "/services/data/v38.0/wave/datasets/0FbB00000000pNNKAY"
                    }
                ],
                "isFacet": false,
                "isGlobal": false,
                "query": {
                    "measures": [
                        [
                            "sum",
                            "Amount"
                        ]
                    ],
                    "groups": [
                        "Owner.Name"
                    ],
                    "filters": [
                        [
                            "Owner.Name",
                            "{{column(Owner_Name_1.selection, [\"Owner.Name\"]).asObject()}}",
                            "in"
                        ]
                    ],
                    "order": [
                        [
                            -1,
                            {
                                "ascending": false
                            }
                        ]
                    ]
                },
                "selectMode": "multi",
                "type": "aggregateflex",
                "useGlobal": true,
                "visualizationParameters": {
                    "visualizationType": "hbar",
                    "options": {}
                }
            }


Sample-3:
 "QueryLoginUser_1": {
                "groups": [],
                "numbers": [],
                "query": "SELECT Name FROM User Where Name = '!{User.Name}'",
                "selectMode": "single",
                "strings": [],
                "type": "soql"
            }

use the query result into a step, in this sample for filter:

[
   "User.Name",
   "{{coalesce(cell(User_Name_1.selection,0,\"User.Name\"), cell(QueryLoginUser_1.result,0,\"Name\")).asString()}}",
   "in"
]



ReferenceDynamically Set Initial Selections Based on the Dashboard Viewer



Friday, October 11, 2019

Einstein Analytics: Transpose data from rows to columns

You know, in life you face multiple weird things, the same when you work as Einstein Analytics consultant. In the previous blog, we share how to transpose data from columns to rows.



In this blog, the requirement is the other way rounds.

Background: we need to show child records on the parent level, the number of rows in the table should follow the number of parents, and the good news is, there is a limit of children for a parent, and for this case, the max child is 5.

Solution: use a lot of computeRelative transformation nodes.



Let us go through each computeRelative nodes:

Node-1
- Partition By = Country
- Order By = City ascending

Add 3 fields here:
- IsFirst with SAQL: case when current(City)==first(City) then "Yes" else "No" end
- Ke_1 with Source Field = City and Offset Function = First
- Ke_2_temp with SAQL: case when previous(City)==first(City) then current(City) else "" end


Node-2
- Partition By = Country
- Order By = Ke_2_temp descending

Add a field here:
- Ke_2 with Source Field = Ke_2_temp and Offset Function = First


Node-3
- Partition By = Country
- Order By = City ascending

Add a field here:
- Ke_3_temp with SAQL: case when current(City)==Ke_2 then next(City) else "" end


Node-4
- Partition By = Country
- Order By = Ke_3_temp descending

Add a field here:
Ke_3 with Source Field = Ke_3_temp and Offset Function = First


Repeat above until Node-8, only field in yellow highlight will be used, and the one end with _temp will be drop, we just use it as helpers.

Use filter node to drop all rows without IsFirst == "Yes", and slide node to drop all fields except Country and the ones in yellow.

Here is the source


Here is the result






Wednesday, October 9, 2019

Salesforce Einstein – Where to start to experiment and understand Machine Learning?


by Jean-Michel Mougeolle, Salesforce MVP hall of fame, Salesforce Einstein Champion, SharinPix CEO.


What is the Einstein Champion program?
Let me start this blog with the Einstein Champions Program. The Einstein Champions Program is for Trailblazers that are passionate about the Einstein Platform and want to share their advanced knowledge with peers and evangelize the power of Einstein.

I have the chance to be part of those, certainly, due to the various have made around Einstein Vision at Dreamforce and in many Dreamin’ events. I’m convinced that Einstein Vision is a great way to start learning with Machine Learning in Salesforce.


Why starting by Einstein Vision?
First, it will make you understand very easily the benefits and the approach required by Machine Learning.

Second, you can easily play with it, FOR FREE!
For Free? You mean you don’t need any licenses?
No, you just have to install the Einstein Vision and Language Model Builder by Salesforce Labs, to start playing with it. The creation of models is free, and to test them you have up to 2000 predictions per month for free as well.




So where should we start to create our first model?
I will go with Einstein Vision Image Classification. It only takes a zip file with few images organized by labels in folders to start with something. Of course, you may have to gather enough images per label to get something working, and take care of image format, size and resolution. But if you plan only to create a model for testing, extracting images from some google search should be sufficient to have nice results.


Can you explain the basis of Image Classification?
Yes, for sure, Image Classification makes prediction to identify a picture from examples on which it has been trained. As an example, the model can recognize a cat from a dog if it has been well trained with enough dogs and cats pictures.


For our demo jam with SharinPix we have used images from google to create models to classify food pictures. The model can recognize hot-dog, pizza, burger, drinks, dessert, BBQ meat and more. That’s a good example on how to classify from image line of a menu to make them sorted automatically.



You mean that you can train a model that easily?
Yes, you just have to catalog enough images per label (100), construct a zip file with those and create a dataset with it. The UI from the Salesforce Lab package allows you to easily create a dataset from a zip file. Once you have a dataset, you can train a model from the same package. The model is the « engine » to create predictions.
Once you have a model, you can present a picture and the model will make prediction.


What can we expect to learn from that?
The limits of a poor dataset.  As an example, if you upload only white cats and only black dogs in a dataset, you will get a bad quality dataset. If you present then a black cat to it, it will certainly predict it as a dog.

Getting a good dataset is key, and it’s really easy to understand from example that is not working. As Image Classification is very visual, you can learn easily about the right and wrong approach around Machine learning.


What about Object Detection?
It’s quite the same principle than Image Classification, but it can detect many objects in a picture and get back with the position, the numbers and of course the probability associated to each recognition. The main usage for this is to automate retail execution from Shelf Display pictures.




Is that as easy as for the Image Classification?
Yes and no.
It doesn’t require different technology and it’s the same approach: create a dataset with pictures and train a model to get prediction. But if you need to label the pictures with bounding boxes representing all the objects you want to recognize.

So, in the example of retail execution, you may have to make it learn from shell display images where you have to draw boxes around each object you want to recognize, with the name of it. And this time you don’t need 100 pictures per label, but 200 bounding boxes per label across all the pictures used in the dataset. And the drawing of the box requires to be precise for a good prediction.


What are the main problems that can make you have a bad dataset?
The first is the bad quality of labeling. AI is basing is logic on the examples you feed it with. If you give him wrong examples, it will result in bad predictions. When you label hundreds of images, it’s easy to make mistakes. There, QA is mandatory to avoid any errors in the labeling.
The second, the diversity, frequency, and quality of images are key. You should not use images too angled or with too much light. And you may need as well to get the same frequency for each object to recognize across all the images in the dataset.


You seem to be very well experienced around that, does it come from what you have done with SharinPix?
Yes, we have provided the services to create tons of models for various big retail customers, but also from the company in other industries. We have labeled datasets that can recognize multiple hundreds of objects and with multiple thousands of images.

The quality approach is key in that kind of project, getting organized, having the right level of QA and a good understanding of the risk for each problematic met is really important.
We have constructed an app to help the team that wants to be serious about model making, model optimization, and model maintenance. We use it internally and provide the services around worldwide too many different companies.


Is that available on the AppExchange?
Yes, it’s part of the SharinPix App, but you can reach me for any question about Machine Learning and the app whenever you need!


So, can you recap the best thing to start with if you want to learn about Einstein?
Yes, the first one is if course trailhead, there is an incredible TrailMix that will make you learn a lot: https://sfdc.co/einsteinchampionstrailmix 

Then you can install the Model Builder provided by Salesforce Labs from the AppExchange:
https://appexchange.salesforce.com/appxListingDetail?listingId=a0N3A00000Ed1V8UAJ

And of course, if you want some help and get serious about Image Recognition you can rely on SharinPix App and Labelling Services: http://bit.ly/SharinPixAppExchange



Friday, October 4, 2019

Salesforce: SOQL Picklist Values & API Name

As you are aware that we can have different names between Picklist Values & API Name in Salesforce, see this screenshot:



When users enter the data or run a report, they will only see Values and not API Name.



When admin or developer do a query with SOQL, the result is API Name
SELECT Id, Name, AccountSource FROM Account WHERE AccountSource <> ''



If you need to get the values from SOQL, use tolabel() function. Here is the updated query
SELECT Id, Name, toLabel(AccountSource) FROM Account WHERE AccountSource <> ''




ReferenceTranslating Results



Sunday, September 29, 2019

Einstein Analytics: Transpose data from columns to rows

In the table-1 below; Data-1, Data-2, and Data-3 are stored as individual columns.



Here is what we can get from Table-1



But, we can't have a total of A, B, and C as a single bar chart for easy comparison, the easiest option is to change the dataset into Table-2.



Solution: use dataflow to expand the rows and create new fields with combine values


Notes:
- ceData1,ceData2,ceData3 contain 2 compute fields: Type and Value
   - Type_Data_1 = "Data-1"
   - Value_Data_1 = Data_1
   - same goes for ceData2 and ceData3

- In appendAllData, select "Allow disjoint schema"

- ceData also contain compute field Type and Value, and using Case in the formula

for Type_Data
case
  when Type_Data_1 is not null then "Data-1" 
  when Type_Data_2 is not null then "Data-2"
  when Type_Data_3 is not null then "Data-3"
end

for Value_Data
case
  when Type_Data_1 is not null then Data_1
  when Type_Data_2 is not null then Data_2
  when Type_Data_3 is not null then Data_3
end


Here our result:





Monday, September 9, 2019

Salesforce: Query Multi-Currency Field

When you have multi-currency enabled in your Salesforce org., by default you will see the converted value in the page layout, list view, and report.

Page Layout
My user currency is USD and Corporate currency is USD too.



If I change my currency to SGD



List View
The same goes to List View, it will show the amount in the opportunity currency and the value in your currency in brackets.


Report
In the report, Salesforce gives the option to get the values in the Opportunity currency and in the converted amount -- which is the corporate currency.



SOQL
When you query Salesforce currency fields, SOQL will always return currency values as defined in the CurrencyIsoCode.



Format() and convertCurrency()
We can use Format() and convertCurrency() functions for currency field in SOQL:
- Use FORMAT with the SELECT clause to apply localized formatting to standard and custom number, date, time, and currency fields, the format applied these fields reflect the appropriate format for the given user locale.
- Use convertCurrency() in the SELECT clause to convert currency fields to the user’s currency.

Looks at this sample: SELECT Id, CurrencyIsoCode, Amount, convertCurrency(Amount) UserAmount, FORMAT(amount) TextAmount, FORMAT(convertCurrency(amount)) convertedCurrency FROM Opportunity order by currencyisoCode



You can’t use the convertCurrency() function in a WHERE clause. If you do, an error is returned, but you can use the following syntax to convert a numeric value to the user’s currency from any active currency in your org. WHERE Object_name Operator ISO_CODEvalue

e.g. SELECT Id, Name FROM Opportunity WHERE Amount > USD5000
In this example, opportunity records are returned if the record’s currency Amount value is greater than the equivalent of USD5000. For example, an opportunity with an amount of USD5001 is returned, but not JPY7000.



How to get currency value in corporate currency using SOQL?
In Spring ’18 release, Salesforce introduces new formula functions ADDMONTHS, CURRENCYRATE, MCEILING, MFLOOR and WEEKDAY. So this purpose, we can make use of CurrencyRate() function.

CURRENCYRATE returns the conversion rate to the corporate currency for the given currency ISO code. If the currency is invalid, returns 1.0.

We can create a simple formula field
Amount / CURRENCYRATE(TEXT(CurrencyIsoCode))

Here is the result:


If you implement dated exchange rate, CurrencyRate() function do not support it yet, it will always use standard exchange rate, unless you always keep the conversion rate aligned.



Reference:


Tuesday, September 3, 2019

Einstein Analytics: Result Binding

There are 2 types of binding in Einstein Analytics:
1. Selection: the query result based on user selection
2. Result: the query result based on the changes in other steps

We have discussed selection binding in the previous blogs: here, here, and here. Now let us looks at result binding.

Here is the sample for this blog:
- We have 2 datasets which are not really linked, but both have user Id and Fiscal
- Opportunity Split dataset have Close Date, which can be used as a toggle


Steps:
1. Create Static Step to have Previous Quarter, Current Quarter and Next Quarter
                            "Static_Period_1": {
                "broadcastFacet": true,
                "label": "Static_Period",
                "selectMode": "singlerequired",
                "start": {
                    "display": [
                        "Current Quarter"
                    ]
                },
                "type": "staticflex",
                "values": [
                    {
                        "display": "Previous Quarter",
                        "value": -1
                    },
                    {
                        "display": "Current Quarter",
                        "value": 0
                    },
                    {
                        "display": "Next Quarter",
                        "value": 1
                    }
                ]
            }

2. For wizard for Opportunity Split dataset, add selection binding to filter the result based on the toggle.
           "query": {
                    "values": [
                        "Fiscal",
                        "Id",
                        "User_Id",
                        "Close_Date",
                        "Amount"
                    ],
                    "filters": [
                        [
                            "Close Date",
                            [
                                [
                                    [
                                        "fiscal_quarter",
                                        "{{cell(Static_Period_1.selection,0,\"value\").asString()}}"
                                    ],
                                    [
                                        "fiscal_quarter",
                                        "{{cell(Static_Period_1.selection,0,\"value\").asString()}}"
                                    ]
                                ]
                            ],
                            ">=<="
                        ]
                    ],
                    "order": [
                        [
                            "Fiscal",
                            {
                                "ascending": true
                            }
                        ]
                    ]
                }

* Static_Period_1 is the step name for static step, see (1)

3. For wizard for Forecast Quota dataset, add result binding to filter the result based on the column in the Opportunity Split step.
          "query": {
                    "values": [
                        "Fiscal",
                        "User_Id",
                        "Quota"
                    ],
                    "filters": [
                        [
                            "Fiscal",
                            [
                                "{{cell(lens_1.result,0,\"Fiscal\").asString()}}"
                            ],
                            "in"
                        ]
                    ],
                    "order": [
                        [
                            "Fiscal",
                            {
                                "ascending": true
                            }
                        ]
                    ]
                }

* Lens_1 is the step name contains selection binding, see (2)
* Fiscal is the column name in Lens_1 step


For this blog sample, when user selects "Next Quarter" in toggle, the Opportunity Split data will be filtered based on selection defined in static step, then Forecast Quota data will be filtered based on Fiscal changed in Opportunity Split step.



Here is the complete JSON file.



ReferenceResult Binding



Sunday, September 1, 2019

Salesforce: Forecast with Opportunity Split

This is the continuation of previous blog Setup Forecast Quota, in this blog, we will share about Forecast with Opportunity Split Overlay.

1. Opportunity Team and Opportunity Splits


Here is the scenario:
Two users, both users have been enabled for Forecast
1. Johan Forecast is the manager
2. George Mann report to Johan Forecast

Two closed-won opportunities with Overlay split:

opportunity #1

opportunity #2


2. Forecast
Let us the result see in the Forecast tab:


Highlight legend:
- Yellow: this is quota as we discussed in the previous blog
- Pink: this is the forecast item for Closed forecast
- Green: this is summary from all subordinates, including the manager
- Blue: USD 305,500.00 comes from opportunity #2 which is the total amount for the 2nd and 3rd; USD 75,000.00 is from opportunity #1


3. SOQL
SELECT Id, OwnerId, ForecastAmount, Owner.Name, PeriodId, ForecastingTypeId FROM ForecastingItem WHERE ForecastCategoryName = 'Closed' AND ForecastingTypeId = '0Db2v000005aTHcCAM'


Summary:
  • The Forecast Amount in ForecastingItem is auto-populated from Opportunity Split Amount based on the Forecast Type and Forecast Category.
  • The Period Id in ForecastingItem is auto-populated based on Opportunity Closed Date.
  • Each user will only have 1 line for ForecastingItem in a period for a Forecast Type.
  • If you have multi-currencies enabled, only 1 currency will be returned in the query result, this currency is based on defined Corporate Currency.
  • The Forecast Amount from ForecastingItem in SOQL is roll-up to the manager, and managers numbers will roll-up to the managers' manager.

Forecast Manager in Forecast Hierarchy

Sample:
There are 2 users under Staff 1: Song Lee & Free Man

Forecast tab

SOQL
SELECT Id, OwnerId, ForecastAmount, Owner.Name, PeriodId, ForecastingTypeId FROM ForecastingItem WHERE ForecastCategoryName = 'Closed' AND ForecastingTypeId = '0Db0k000000076kCAA' ORDER BY ForecastAmount



What will happen if we remove Maria Ann as Forecast Manager for the role hierarchy General Manager?

Forecast tab

Notice:
1. The Amount is no longer rollup from Maria Ann subordinates'. Forecast managers see forecast rollups from users below them in the forecast hierarchy.
2. Click the arrow next to label 'Maria Ann' will not drill down to the subordinates level.
3. Jack Bob numbers are only from Linda Yie as Maria Ann not contributing anything.
4. The number from Song Lee and Free Man are not roll-up to anyone.

SOQL
SELECT Id, OwnerId, ForecastAmount, Owner.Name, PeriodId, ForecastingTypeId FROM ForecastingItem WHERE ForecastCategoryName = 'Closed' AND ForecastingTypeId = '0Db0k000000076kCAA' ORDER BY ForecastAmount



If Maria Ann have her owned Opportunity Splits


Same for SOQL result, Maria Ann will have USD 18,000.00 for the Forecast Amount.


Now, let us remove Allow Forecasting from Maria Ann

Maria Ann does not appear in the Forecast tab at all.



SOQL:


Remember that Maria Ann still owned Opportunity Split, but she does not appear in both Forecast and SOQL.


Summary:
1. Allow Forecasting is a must for all forecast users.
2. Each manager role in the forecast hierarchy should have a user assigned as the Forecast Manager.
3. The API name for "Allow Forecasting" is ForecastEnabled, you can mass update it with API.



Page-level ad