Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
reinaldoabe1
Explorer
This blog post will demonstrate how you could create a state-of-the-art SAC landing Analytic Application, which will enable users to drive their planning processes consistently based on a filter line. 

What's a landing page?


Landing Page example


I'm a firm believer in landing pages for Planning processes. It's a great way to create a seamless experience for planners.

A landing page is a standalone story or Analytic application in SAC context, designed with a single focus or goal. In a planning context, a goal could be to perform the budgeting processes for your projects or plan your workforce costs for the FY22. 

SAC standard content library includes some great content with this concept in mind. Workforce Planning is one of them. I would highly recommend activating this content and having a look. 

To take this approach to the next level, I will demonstrate how to create a SAC Analytic Application that includes a filter line and pass these filter values to the SAC stories. 

For context, I would suggest looking into the references below: 

The blog post above covers the concept of passing selected dimension values from a widget to a Story as a filter. In my case, I'm taking this example and adopting a filter line, which needs further scripting as end-users can dynamically change it based on the underline model. 

To start, what I would recommend:

  • Obviously, some basic Javascript knowledge. If I was able to put it off, you should be just fine

  • Activate the Analytic Application "Learn how to set variables and/or filters and open the receiver application" (Under Analytics Designer Features). I used the coding from this application as a baseline for this development. 


Step 1: Add a chart or table to act as a data source 

As the filter line widget requires a source Widget, you have to add a chart or a table in the Analytic Application. Use the chart or table, like I did, to your benefit by providing a high-level overview of your planning KPI's.

In my example, I named my table as "Table".  

Step 2: Add the filter line



Filter Line Properties


Step 3: Create Script variables

These script variables act as Global variables, thus used across Script objects:



 

 































































Script Variables Type Array
Dim_array String X
Hier_array String X
ModelID String
Opt_array String X
OverallCount Integer
PageID String
StoryID String
Unbooked_array String X
URLParam_filters String
URLParam_Variables String
Val_array String X

 

Step 4: Create object scripts

Create the following object scripts.


Code samples:

To identify the Story ID, Model ID and Page ID, I'd suggest looking into the help page below:

function ReportIDMapping(ButtonCall: String): void
//function maps the story, model and page ID based on the button name
switch(ButtonCall)
{
case "Resource Planning":
StoryID = <StoryID>;
ModelID = <ModelID>;
PageID = <PageID>;
break;
case "Simulate Depreciation":
<<add your parameters here>>
}

Utils.openReceiverApp(StoryID, ModelID, PageID);

 
function openReceiverApp(StoryID: String, ModelID: string, PageID: string): void
//function executes the URL

var ds = Table.getDataSource();
var separator = "|";

Dim_array = [""];
Hier_array = [""];
Opt_array = [""];
Val_array = [""];
OverallCount = 0;
Unbooked_array = [""];
UrlParam_Filters ="";
UrlParam_Variables = "";

Utils.getUrlParam_Filters(ds);

//if any selection is performed
if (OverallCount > 0)
{
var urlparam = UrlParameter.create("f01Model",ModelID);
var url_array = [urlparam];

//dimension array
for (var i = 0; i < Dim_array.length; i++) {
var val_values = Dim_array[i].split(separator);
//console.log(["urlarray1",val_values,]);
urlparam = UrlParameter.create(val_values[0],val_values[1]);
url_array.push(urlparam);
}

//opt array
for (i = 0; i < Opt_array.length; i++) {
val_values = Opt_array[i].split(separator);
urlparam = UrlParameter.create(val_values[0],val_values[1]);
url_array.push(urlparam);
}

//values array
for (i = 0; i < Val_array.length; i++) {
val_values = Val_array[i].split(separator);
//console.log(["urlarray3",val_values,]);
urlparam = UrlParameter.create(val_values[0],"["+val_values[1]+"]");
url_array.push(urlparam);
}

//hierarchy array
for (i = 0; i < Hier_array.length; i++) {
val_values = Hier_array[i].split(separator);
//console.log(["urlarray3",val_values,]);
urlparam = UrlParameter.create(val_values[0],val_values[1]);
url_array.push(urlparam);
}

//unbooked array
for (i = 0; i < Unbooked_array.length; i++) {
val_values = Unbooked_array[i].split(separator);
//console.log(["urlarray3",val_values,]);
urlparam = UrlParameter.create(val_values[0],val_values[1]);
url_array.push(urlparam);
}
}

NavigationUtils.openStory(StoryId,PageID,url_array, true);




 
function getURLParam_Filters(ds: datasource): void
// function loops through dimension filters and execute
var dimensions = ds.getDimensions();

for (var i=0;i<dimensions.length; i++){
//get dimension
var dimension = dimensions[i];
//get dimensionfilters
var filters = ds.getDimensionFilters(dimension);
// check filter values and if Measure is filtered
if (filters.length > 0 && dimension.id !== "@MeasureDimension") {
//console.log(["Filters", filters,]);
var hierarchy = ds.getHierarchy(dimension).id;
//get filter function
Utils.getFilter(dimension.id,filters,hierarchy,i);
}
}


function getFilter(dimension: string, filters: FilterValue[], hierarchy: string, dimindex: integer): void
//Function populates Dim, Hier, Opt and Val arrays

var s = "";
var v_separator = "|";
var v_dim = "";
var v_opt = "";
var v_hier = "";
var v_unbooked = "";
var v_optvalue = "in";

// count required for parameters
if ( filters.length > 0 )
{
OverallCount = OverallCount + 1;
}

for (var i = 0; i < filters.length; i++) {
var filter = filters[i];

// create variable to store dimension parameters for URL
var v_str = ConvertUtils.numberToString(OverallCount);

//dimension parameters
v_dim = "f0" + v_str + "Dim";
Dim_array.push(v_dim + v_separator + dimension);

//hierarchy parameters
v_hier = "f0" + v_str + "Hierarchy";
Hier_array.push(v_hier + v_separator + hierarchy);

//unbooked parameters
v_unbooked = "f0" + v_str + "Unbooked";
Unbooked_array.push(v_unbooked + v_separator + "true");

if (filter.type === FilterValueType.Single) {

// handle single filter value
var singleFilter = cast(Type.SingleFilterValue, filter);

//opt parameters
if (singleFilter.exclude === true)
{
v_optvalue = "notIn";
} else
{
v_optvalue = "in";
}

v_opt = "f0" + v_str + "Op";

Opt_array.push(v_opt + v_separator + v_optvalue);

if (hierarchy !== "@FlatHierarchy")
{
//split values
var v_trim = singleFilter.value.split("[").join("");
v_trim = v_trim.split("]").join("");
v_trim = v_trim.split("&").join("");

if (dimension === "Date") //Date comes in a different structure
{
var array = v_trim.split(".");
s = s + '"' + array[4] + '"';

}else
{
array = v_trim.split(".");
s = s + '"' + array[2] + '"';
}

} else
{
s = s + '"' + singleFilter.value + '"';
}

} else if (filter.type === FilterValueType.Multiple) {

// handle multiple filter value
var multipleFilter = cast(Type.MultipleFilterValue, filter);
var values = multipleFilter.values;

//opt parameters
if (multipleFilter.exclude === true)
{
v_optvalue = "notIn";
} else
{
v_optvalue = "in";
}

v_opt = "f0" + v_str + "Op";

Opt_array.push(v_opt + v_separator + v_optvalue);

for (var j = 0; j < values.length; j++) {
if (j > 0) {
s = s + ",";
}

// check hierarchy selection
if (hierarchy !== "@FlatHierarchy")
{
//split values
v_trim = values[j].split("[").join("");
v_trim = v_trim.split("]").join("");
v_trim = v_trim.split("&").join("");

if (dimension === "Date") //Date comes in a different structure
{
array = v_trim.split(".");
s = s + '"' + array[4] + '"';

}else
{
array = v_trim.split(".");
s = s + '"' + array[2] + '"';
}
} else //flat presentation
{
s = s + '"' + values[j] + '"';
}
}

}
Val_array.push("f0" + v_str + "Val" + v_separator + s);
}
//if you want to print the arrays
//console.log(["val_array",Val_array,]);
//console.log(["Dim_array",Dim_array,]);
//console.log(["opt_array",Opt_array,]);
//console.log(["Hier_array",Hier_array,]);

Step 5: Create a button


If you notice in the function ReportIDMapping, the case statement is based on the text of the button. In the example below, the button description "Resource Planning" is sent as a parameter to the "ReportIDMapping function.


Step 6: Add "OnClick" script to the button 
function OnClick() void

Utils.ReportIDMapping(ResourcePlanning.getText());

 

Voila! Your Analytic Application is now sending the filter parameters dynamically to your story.


 

 

Summary:

From a technical perspective, you have learned how to create a simple Analytic Application that includes: a filter lane, table, and a button that executes a set of Javascript functions calling a destination story.

Moreover, you learned how landing pages could support your customers in taking full advantage of SAC, by enabling a seamless and simplified experience.

I hope you can leverage this example to many other use cases, exploring the many advantages of SAC Analytic Application API's.

Lastly, I would love to hear your comments and feedback below. For any further technical questions, I would recommend having a look or post questions on the SAC community questions page.

 

Key references:

Help SAP - Filters Parameters guide

Help SAP - Open Story URL API

Help SAP - Using Result Set APIs (with code samples)

SAP Analytic Application Developer Handbook

 

Hope this can enlighten your day!

 

 

 

 

 

 
4 Comments
Labels in this area