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: 
bastianbrindley
Explorer
0 Kudos

Introduction:


The purpose of this blog is to showcase the usage of a multidimensional array with the SAC Analytics Designer scripting APIs.

More specifically, in relation to calendar tasks as here the built-in functionalities were lackluster to me.
The greater context is to retrieve information of calendar tasks for the current planning cycle ordered by the sequence of steps.

Setup:


There are various calendar tasks existing on a planning model. We want to access and use a subset of these based on their names (and sorted by the name).

Problem:


The IDs of the calendar tasks can be retrieved through the calendar integration feature. Also, the names of these tasks can then be accessed.
However, there is no object to filter and sort by name and still have the ID available.
This could be achieved with multiple variables and looping multiple times through them, but it would be more efficient if we would have a two-column array with both information available.

The latter solution is displayed below.

Implementation:


The general possibility of working with multidimensional arrays in the Designer is depicted in the Developer Handbook (https://d.dam.sap.com/a/3Y16uka/DeveloperHandbookSACAnalyticsDesigner_v12.1_final.pdf) - in Version 12.1 it can be found in chapter 4.4.14.
As in other cases the functionalities of the Application Design JavaScript dialect is not as powerful as in standard JavaScript developments, but with some workarounds a good solution can be achieved.

In our use case with calendar tasks we will first retrieve some example tasks:
var rel_tasks = CI_1.getRelatedTaskIds();

That gives us the following 8 entries


rel_tasks


Now we can create the 2-dimensional array with 8 rows (based on the length of "rel_tasks"):
var numRows = rel_tasks.length;
var arr1D = ArrayUtils.create(Type.string);
var arr2D = [arr1D];

for (var i = 1; i < numRows; i++) {
arr2D.push(ArrayUtils.create(Type.string));
}

The array "arr2D" is now an empty 2 column, 8 rows array of string type.

The next part is the crucial operation, for better understanding find here the names of our 8 example values in the order of their IDs:

STEP1_2022
STEP4_2022
STEP3_2022
STEP2_2022
STEP4_2023
STEP2_2023
STEP3_2023
STEP1_2023

The sorting can be considered random - for our purpose we want to have the IDs accessible for the tasks ending with "2023" being sorted ascending by the "STEP" sequence.

Let us take look at the coding:
var arr_row = 0;
for (i=0;i<numRows;i++){
//make task properties available
var task_id = CI_1.getCalendarTaskById(rel_tasks[i]);
//check if name is in the required scope
if(task_id.getName().substr(6,4) === "2023"){
//store name in first column
arr2D[arr_row][0] = task_id.getName();
//store ID in second column
arr2D[arr_row][1] = task_id.getId();
//store next relevant entry into next row
arr_row = arr_row + 1;
}
else{
//remove rows of non-relevant entries
arr2D.splice(i,1);
}
}

We are looping over the "rel_tasks" IDs and accessing their properties, e.g., the task name.
Then we check if the name is relevant for our purpose (2023), if so, the name is stored into the first column of "arr2D" (as we want to use the name for sorting) and the ID into the second column.

Furthermore, we are cleaning up the array by the usage of ".splice". That means our initially as 8 rows array created variable will be reduced to the 4 rows that we require.

Important to see is that the respective cell is addressed by its index in the square brackets like arr2D[row_index][column_index].

 

Please note that the above could also be combined into a single loop that creates the array internally, but for the purpose of showcasing the array creation itself I splitted it above. See here the more compact coding:
var rel_tasks = CI_1.getRelatedTaskIds();
var arr1D = ArrayUtils.create(Type.string);
var arr2D = [arr1D];

var arr_row = 0;
for (var i=0;i<rel_tasks.length;i++){
//make task properties available
var task_id = CI_1.getCalendarTaskById(rel_tasks[i]);
//check if name is in the required scope
if(task_id.getName().substr(6,4) === "2023"){
//first row already exists in array
if(arr_row>0){
arr2D.push(ArrayUtils.create(Type.string));
}
//store name in first column
arr2D[arr_row][0] = task_id.getName();
//store ID in second column
arr2D[arr_row][1] = task_id.getId();
//store next relevant entry into next row
arr_row = arr_row + 1;
}
}

 

With both approaches we are now retrieving the 4 relevant rows:


unsorted result array


 

Now we just need to sort the array:
arr2D.sort()

and we have the required tasks in the correct order:


sorted result array


 

That was already the main objective of this blog, but let us now take a look on the further usage of the sorted result set:
V_preceding = "";

for(i=0;i<arr2D.length;i++){
switch(arr2D[i][0].substr(0,5)){
case "STEP1":
ScriptObject_1.DERIVE_CAL_TEXT(Text_S1, CI_1.getCalendarTaskById(arr2D[i][1]));
break;
case "STEP2":
ScriptObject_1.DERIVE_CAL_TEXT(Text_S2, CI_1.getCalendarTaskById(arr2D[i][1]));
break;
case "STEP3":
ScriptObject_1.DERIVE_CAL_TEXT(Text_S3, CI_1.getCalendarTaskById(arr2D[i][1]));
break;
case "STEP4":
ScriptObject_1.DERIVE_CAL_TEXT(Text_S4, CI_1.getCalendarTaskById(arr2D[i][1]));
break;
}
}

 

Since the relevant tasks are sorted by the step sequence, we can incorporate a detection of preceding ongoing tasks (for this we use the global variable "V_preceding").
We handover text fields for each of the steps as well as the correlating task ID to a script object "DERIVE_CAL_TEXT", which is supposed to fill text fields with the required information:

 


DERIVE_CAL_TEXT



if(V_preceding==="X"){
text_field.applyText("Preceding Task still ongoing");}
else{
switch(task_id.getStatus()){
case CalendarTaskStatus.Successful:
text_field.applyText("Completed");
V_preceding = "";
break;
default:
var curr_date = new Date(Date.now());
if(task_id.getDueDate()<curr_date){
text_field.applyText("Overdue since: " + task_id.getDueDate().toDateString());
}
else{
text_field.applyText("Due by: " + task_id.getDueDate().toDateString());
}
V_preceding = "X";
break;
}
}

 

The output of this implementation are text fields which can give the user insight into the status of their task To-Dos in relation to the current planning process:


Output


 

Furthermore, and depending on the task type, it is possible to enable follow-up options like submitting, approving, declining and so on directly next to the output with the corresponding task ID associated.

Conclusion


To me, this has eased out working with multiple calendar tasks in the application designer. The downside is that there needs to be a clear planning process and naming convention for the calendar tasks, but this should be the best practice anyway 🙂

Let me know if You have any questions or something is unclear, I´ll be happy to enhance the post.

 
3 Comments
Labels in this area