1. Introduction
To address this pain-point, I built a story-based application powered by the SAC Calendar APIs that delivers true “point-to-point” bulk editing of event context. Users simply enter the ID of a process; the app renders the full hierarchy—parent process, sub-processes, and child events—so the exact scope for modification can be pinpointed in seconds. Next, they can flexibly pick the target model, the context dimension that needs adjustment, and the new member(s) to be assigned. The chosen dimension is updated across all selected events in one shot, while every other dimension remains untouched, ensuring no downstream re-work or loss of existing settings.
This capability is especially valuable when:
• A standard-template process has been generated and a single dimension (e.g., time, region) must be aligned rapidly across the entire structure.
• Organizational or strategic shifts require only a targeted context revision—no need to re-initialize the entire configuration.
2. How to Use the “Calendar Event Context Batch Modification” Application
• Read access to public files
• Read access to planning models
Select the event → click Details (top-right corner) → in the pop-up menu bar, click More (top-right again) → choose Event Settings.
If the “Allow External API Access” checkbox is not selected, please select it now.
Then click “Show Details” to view and copy the Event ID.
3. Paste the Event ID into the text box and click “Confirm”; the structure of the process and its sub-events will then be displayed in the next panel.
4. In the next panel, select the scope you want to modify and click “Confirm”.
Please note that not every selected event will necessarily be updated; actual modification depends on whether the event (1) uses the model you subsequently choose and (2) contains the dimension you subsequently specify.
5. In the next panel, select the model that the context you want to modify references, then click “Confirm”.
6. In the next panel, choose the dimension you want to modify, then click “Confirm”.
7. In the next panel, update the dimension members as follows:
Click one dimension.
Proceed as shown in the example below.
8. Click “Confirm” if every dimension is ready.
9. Once the changes are applied, the right-hand panel will display the execution log.
10. To modify additional sub-events, simply return to Step 4 and repeat the process.
3. Important Notes
The following task types cannot be replaced:
• Data Locking tasks
• Data Action tasks
• Multi-Action tasks
• Input tasks
4. Codes
• Member – an individual instance or category item under a given dimension.
• Filter – a combination of one dimension plus one or more of its members, used to narrow the data to a desired analytical scope.
Before the feature can run, enable “Calendar Integration” in the story app and initialize the required variables. Key variables are listed below:
// Reference to the DataSource object of the chosen model
datasource
// Array of IDs for the parent process and all its descendants
eventsIds
// Boolean flag indicating whether every event in the hierarchy is selected (used by the “Select All” toggle)
isEventsAllSelected
// ID of the model whose context is being edited
modelId
// Array holding every filter that involves the currently selected dimension across all chosen events
// Example: if dimension A is selected, all filters containing A are pushed here
selectedFilters
// Array of event objects that will actually be modified after all filtering rules are applied
toBeModifiedEvents
// ———————————————————-
// The next two arrays act together like a 2-D map:
// index i links modelId → dimensionId
// ———————————————————-
uniqueModelDimensionId_1 // Stores model IDs
uniqueModelDimensionId_2 // Stores the corresponding dimension IDs for each model
During page initialization, the system-message notification types are configured (e.g., success, warning, error).
2. Validate and retrieve the process with all its children
//获取event ID。Get the event ID.
var userInput_processID = InputField_1.getValue();
var processEvent = CalendarIntegration_1.getCalendarEventById(userInput_processID);
//如果获取不到对象,则报错。 If the object cannot be obtained, an error will be reported.
if (processEvent === undefined) {
Application.showMessage(ApplicationMessageType.Warning, “Please enter a correct ID.”);
Application.hideBusyIndicator();
return;
}
• spaceQueue – stores the depth level so the hierarchy can be rendered clearly
//BFS
var idQueue = ArrayUtils.create(Type.string);
var spaceQueue = ArrayUtils.create(Type.string);
idQueue.push(processEvent.getId());
//spaceQueue以字符的形式标明了目前遍历到第几层。 spaceQueue indicates the current level of traversal in the form of characters.
spaceQueue.push(“”);
while (idQueue.length > 0) {
var currentId = idQueue.shift();
var currentSpace = spaceQueue.shift();
var currentEvent = CalendarIntegration_1.getCalendarEventById(currentId);
if (currentEvent !== undefined) {
CheckboxGroup_1.addItem(currentId, currentSpace + currentEvent.getName());
eventsIds.push(currentId);
if (currentEvent.getType() === CalendarTaskType.Process) {
var currentProcess = cast(Type.CalendarProcess, currentEvent);
var childrenIds = currentProcess.getChildren();
for (var i = 0; i < childrenIds.length; i++) {
idQueue.push(childrenIds[i]);
spaceQueue.push(currentSpace + “-“);
}
}
}
}
3. After the user selects events, extract the unique filters from all chosen events.
• Collect every selected event.
• Loop through each event’s filters and accumulate them into a deduplicated array
• If the resulting array is empty (no filters found), raise an error message.
//获取到选择的事件 Obtain the selected events.
toBeModifiedEvents = ArrayUtils.create(Type.CalendarEvent);
var toBeModifiedEventsID = CheckboxGroup_1.getSelectedKeys();
var uniqueFilters = ArrayUtils.create(Type.CalendarContextFilter);
//获取到有几种不同的筛选器 Obtain the unique filters.
for (var i = 0; i < toBeModifiedEventsID.length; i++) {
var event = CalendarIntegration_1.getCalendarEventById(toBeModifiedEventsID[i]);
toBeModifiedEvents.push(event);
// 获取当前事件的筛选器数组 Obtain the array of filters of the current event.
var filters = event.getContextFilters();
if (filters === undefined) {
continue;
}
console.log(filters);
// 遍历每个筛选器 Traverse each filter.
for (var j = 0; j < filters.length; j++) {
var filter = filters[j];
if (!uniqueFilters.includes(filter)) {
uniqueFilters.push(filter);
}
}
}
//如果找不到任何筛选器,则报错。If no filter is found, an error will be reported.
if (uniqueFilters.length === 0) {
Application.showMessage(ApplicationMessageType.Warning, “No context was found.”);
Application.hideBusyIndicator();
return;
}
4. Retrieve the dimension IDs under each model ID and the mapping between model IDs and model names.
• From the deduplicated-filter array built in the previous step, extract every model ID together with its associated dimension IDs
• For each model ID, obtain the corresponding datasource (via a hidden table) to read the model’s display name.
var uniqueModelId = ArrayUtils.create(Type.string);
for (var m = 0; m < uniqueFilters.length; m++) {
var modelId_ = uniqueFilters[m].members[0].modelId;
var dimensionId = uniqueFilters[m].members[0].dimensionId;
if (!(uniqueModelDimensionId_1.includes(modelId_) && uniqueModelDimensionId_2.includes(dimensionId))) {
uniqueModelDimensionId_1.push(modelId_);
uniqueModelDimensionId_2.push(dimensionId);
}
if (!uniqueModelId.includes(modelId_)) {
uniqueModelId.push(modelId_);
}
}
var temp1 = ArrayUtils.create(Type.string);
var temp2 = ArrayUtils.create(Type.string);
for (var n = 0; n < uniqueModelId.length; n++) {
var modelId__ = uniqueModelId[n];
Table_1.setModel(modelId__);
datasource = Table_1.getDataSource();
temp1.push(modelId__);
temp2.push(datasource.getInfo().modelName);
}
for (var o = 0; o < temp1.length; o++) {
RadioButtonGroup_1.addItem(temp1[o], temp2[o]);
}
5. Once the user selects a model, retrieve the mapping between dimension IDs and their descriptions.
• Match the dimension IDs returned by the Calendar API with those in the model’s datasource to obtain the description for each dimension.
modelId = RadioButtonGroup_1.getSelectedKey();
datasource = Table_1.getDataSource();
var dimensions = datasource.getDimensions();
var descriptions = ArrayUtils.create(Type.string);
for (var h = 0; h < uniqueModelDimensionId_1.length; h++) {
if (uniqueModelDimensionId_1[h] === modelId) {
for (var j = 0; j < dimensions.length; j++) {
if (dimensions[j].id === uniqueModelDimensionId_2[h]) {
descriptions.push(dimensions[j].description);
break;
}
}
console.log(“error, dimension id dismatch”);
} else {
descriptions.push(“1”); //占位
}
}
for (var i = 0; i < uniqueModelDimensionId_1.length; i++) {
if (uniqueModelDimensionId_1[i] === modelId) {
CheckboxGroup_2.addItem(uniqueModelDimensionId_2[i], uniqueModelDimensionId_2[i] + ” ” + descriptions[i]);
}
}
6. After the user selects a dimension, retrieve the specific filters that need to be modified based on the chosen dimension ID.
var selectedDimensions = CheckboxGroup_2.getSelectedKeys();
selectedFilters = ArrayUtils.create(Type.CalendarContextFilter);
for (var k = 0; k < toBeModifiedEvents.length; k++) {
var filters = toBeModifiedEvents[k].getContextFilters();
for (var m = 0; m < filters.length; m++) {
if (selectedDimensions.includes(filters[m].members[0].dimensionId) && filters[m].members[0].modelId === modelId && !selectedFilters.includes(filters[m])){
selectedFilters.push(filters[m]);
}
}
}
7. Add the filters earmarked for modification to the datasource so they can appear in the Filter Line.
• Because the Version dimension is applied as a default filter on the datasource, pre-emptively place Version in the Table’s column axis to prevent this.
var dimensions = datasource.getDimensions();
for (var s = 0; s < dimensions.length; s++) {
if (dimensions[s] == “Version”) {
Table_1.addDimensionToColumns(“Version”);
break;
}
}
for (var o = 0; o < selectedDimensions.length; o++) {
for (var u = 0; u < selectedFilters.length; u++) {
if (selectedFilters[u].members[0].dimensionId === selectedDimensions[o]) {
datasource.setDimensionFilter(selectedDimensions[o], selectedFilters[u].members);
}
}
}
8、emove old filters and add new ones.
• First, deduplicate the previously stored array of filters that need modification.
• Remove the old filters.
• Construct the new Context Filter.
• Add the new filter.
• Append an entry to the log.
var dimensions = datasource.getDimensions();
var selectedFiltersNoRepeat = ArrayUtils.create(Type.CalendarContextFilter);
for (var v = 0; v < selectedFilters.length; v++) {
if (!selectedFiltersNoRepeat.includes(selectedFilters[v])) {
selectedFiltersNoRepeat.push(selectedFilters[v]);
}
}
//selectedFilters 去重 Remove duplicates from selected filters.
for (var q = 0; q < selectedFiltersNoRepeat.length; q++) {
//selectedFilter是目前需要修改的筛选器 selectedFilter is the filter that currently needs to be modified.
var selectedFilter = selectedFiltersNoRepeat[q];
//复制一份变量,避免出现意外情况 Make a copy of the variable to avoid unexpected situations
var modifiedFilter = selectedFilter;
var dimensionId = modifiedFilter.members[0].dimensionId;
var newMemberInfo = ArrayUtils.create(Type.MemberInfo);
//构造一个原有members的字符串,以供日志使用 Construct a string of the original members for logging purposes.
var oldMembers = “”;
for (var r = 0; r < modifiedFilter.members.length; r++) {
oldMembers = oldMembers + ” ” + modifiedFilter.members[r].description;
}
//从datasource中获取到用户已经在filter line中修改好的filter Obtain the filter that the user has already modified in the filter line from the datasource.
var unspecifiedFilterValues = datasource.getDimensionFilters(dimensionId);
//删除旧的筛选器。remove old filter
var recordModifiedEvents = ArrayUtils.create(Type.integer);
for (var j = 0; j < toBeModifiedEvents.length; j++) {
if (toBeModifiedEvents[j].removeContextFilter(selectedFilter)) {
recordModifiedEvents.push(j);
}
}
//构建新的member info Construct the new member info.
for (var m = 0; m < unspecifiedFilterValues.length; m++) {
if (unspecifiedFilterValues[m].type == “Single”) {
var filterValue = cast(Type.SingleFilterValue, unspecifiedFilterValues[m]);
var thisMemberInfo = datasource.getMember(dimensionId, filterValue.value);
newMemberInfo.push(thisMemberInfo);
}
if (unspecifiedFilterValues[m].type == “Multiple”) {
var multipleFilterValue = cast(Type.MultipleFilterValue, unspecifiedFilterValues[m]);
var tempValues = multipleFilterValue.values;
for (var u = 0; u < tempValues.length; u++) {
var thisMemberInfo2 = datasource.getMember(dimensionId, tempValues[u]);
newMemberInfo.push(thisMemberInfo2);
}
}
}
//替换掉原来的member info Replace the original member info.
modifiedFilter.members = newMemberInfo;
//构造一个新members的字符串,以供日志使用 Construct a new string of members for logging purposes.
var newMembers = “”;
for (var e = 0; e < modifiedFilter.members.length; e++) {
newMembers = newMembers + ” ” + modifiedFilter.members[e].description;
}
//增加新的filter,并添加日志 Add a new filter and log the action.
for (var k = 0; k < recordModifiedEvents.length; k++) {
toBeModifiedEvents[recordModifiedEvents[k]].addContextFilter(modifiedFilter);
ListBox_1.addItem(toBeModifiedEvents[recordModifiedEvents[k]].getName()+” [“+modifiedFilter.members[0].dimensionId + “] ” + oldMembers + ” ->” + newMembers);
}
}
5. Conclusion
This article shares the experience I gained while developing the “Bulk Edit Calendar Event Context” story application. If you’re facing similar challenges, I hope it sparks some ideas.
1. Introduction Against the backdrop of accelerated digital transformation and ever-rising demand for agile operations and intelligent decision-making, SAP Analytics Cloud—with its robust, all-in-one capabilities—is rapidly becoming the cornerstone platform for empowering business management and strategic decisions. By seamlessly integrating data visualization, predictive analytics, planning, and collaboration, it not only helps organizations spot trends and optimize processes but also delivers an enterprise-wide, intelligent operating framework. Within SAP Analytics Cloud, the Calendar is heavily used for corporate planning, event scheduling, task tracking, and cross-functional collaboration. Yet its ability to perform bulk edits on event context remains a conspicuous weak spot. For example, once a large-scale process is generated from a standardized template, any subsequent, organization-wide adjustment to a key dimension—such as time or region—can currently be accomplished only by using the “Replace Context” option inside “Edit Event”. This approach, however, wipes out all existing context information, forcing teams to reconfigure every dimension manually. The result is significant human effort and a heightened risk of inconsistencies and governance issues.To address this pain-point, I built a story-based application powered by the SAC Calendar APIs that delivers true “point-to-point” bulk editing of event context. Users simply enter the ID of a process; the app renders the full hierarchy—parent process, sub-processes, and child events—so the exact scope for modification can be pinpointed in seconds. Next, they can flexibly pick the target model, the context dimension that needs adjustment, and the new member(s) to be assigned. The chosen dimension is updated across all selected events in one shot, while every other dimension remains untouched, ensuring no downstream re-work or loss of existing settings.This capability is especially valuable when:• A standard-template process has been generated and a single dimension (e.g., time, region) must be aligned rapidly across the entire structure.• Organizational or strategic shifts require only a targeted context revision—no need to re-initialize the entire configuration. 2. How to Use the “Calendar Event Context Batch Modification” Application Before using the story app, please read the in-story instructions on the same page. 1. Permissions required• Read access to public files• Read access to planning models 2. Obtain the parent calendar process Event ID as follows:Select the event → click Details (top-right corner) → in the pop-up menu bar, click More (top-right again) → choose Event Settings.If the “Allow External API Access” checkbox is not selected, please select it now.Then click “Show Details” to view and copy the Event ID. 3. Paste the Event ID into the text box and click “Confirm”; the structure of the process and its sub-events will then be displayed in the next panel. 4. In the next panel, select the scope you want to modify and click “Confirm”.Please note that not every selected event will necessarily be updated; actual modification depends on whether the event (1) uses the model you subsequently choose and (2) contains the dimension you subsequently specify. 5. In the next panel, select the model that the context you want to modify references, then click “Confirm”. 6. In the next panel, choose the dimension you want to modify, then click “Confirm”. 7. In the next panel, update the dimension members as follows:Click one dimension. Proceed as shown in the example below. 8. Click “Confirm” if every dimension is ready. 9. Once the changes are applied, the right-hand panel will display the execution log. 10. To modify additional sub-events, simply return to Step 4 and repeat the process. 3. Important NotesThe following task types cannot be replaced:• Data Locking tasks• Data Action tasks• Multi-Action tasks• Input tasks 4. Codes Before diving in, let’s clarify three core concepts:• Dimension – a specific aspect or categorical axis within a model.• Member – an individual instance or category item under a given dimension.• Filter – a combination of one dimension plus one or more of its members, used to narrow the data to a desired analytical scope.The following outlines the main implementation logic, broken into steps: 1. Variables & Page InitializationBefore the feature can run, enable “Calendar Integration” in the story app and initialize the required variables. Key variables are listed below:// Reference to the DataSource object of the chosen model
datasource
// Array of IDs for the parent process and all its descendants
eventsIds
// Boolean flag indicating whether every event in the hierarchy is selected (used by the “Select All” toggle)
isEventsAllSelected
// ID of the model whose context is being edited
modelId
// Array holding every filter that involves the currently selected dimension across all chosen events
// Example: if dimension A is selected, all filters containing A are pushed here
selectedFilters
// Array of event objects that will actually be modified after all filtering rules are applied
toBeModifiedEvents
// ———————————————————-
// The next two arrays act together like a 2-D map:
// index i links modelId → dimensionId
// ———————————————————-
uniqueModelDimensionId_1 // Stores model IDs
uniqueModelDimensionId_2 // Stores the corresponding dimension IDs for each model During page initialization, the system-message notification types are configured (e.g., success, warning, error).2. Validate and retrieve the process with all its children //获取event ID。Get the event ID.
var userInput_processID = InputField_1.getValue();
var processEvent = CalendarIntegration_1.getCalendarEventById(userInput_processID);
//如果获取不到对象,则报错。 If the object cannot be obtained, an error will be reported.
if (processEvent === undefined) {
Application.showMessage(ApplicationMessageType.Warning, “Please enter a correct ID.”);
Application.hideBusyIndicator();
return;
} Use a breadth-first search (BFS) with two synchronized queues:• idQueue – stores the event IDs• spaceQueue – stores the depth level so the hierarchy can be rendered clearlyFor each event:Fetch its full object.Bind its properties to the corresponding checkbox.If the event is itself a process, enqueue all of its children (child IDs → idQueue, depth+1 → spaceQueue). //BFS
var idQueue = ArrayUtils.create(Type.string);
var spaceQueue = ArrayUtils.create(Type.string);
idQueue.push(processEvent.getId());
//spaceQueue以字符的形式标明了目前遍历到第几层。 spaceQueue indicates the current level of traversal in the form of characters.
spaceQueue.push(“”);
while (idQueue.length > 0) {
var currentId = idQueue.shift();
var currentSpace = spaceQueue.shift();
var currentEvent = CalendarIntegration_1.getCalendarEventById(currentId);
if (currentEvent !== undefined) {
CheckboxGroup_1.addItem(currentId, currentSpace + currentEvent.getName());
eventsIds.push(currentId);
if (currentEvent.getType() === CalendarTaskType.Process) {
var currentProcess = cast(Type.CalendarProcess, currentEvent);
var childrenIds = currentProcess.getChildren();
for (var i = 0; i < childrenIds.length; i++) {
idQueue.push(childrenIds[i]);
spaceQueue.push(currentSpace + “-“);
}
}
}
} 3. After the user selects events, extract the unique filters from all chosen events.• Collect every selected event.• Loop through each event’s filters and accumulate them into a deduplicated array • If the resulting array is empty (no filters found), raise an error message. //获取到选择的事件 Obtain the selected events.
toBeModifiedEvents = ArrayUtils.create(Type.CalendarEvent);
var toBeModifiedEventsID = CheckboxGroup_1.getSelectedKeys();
var uniqueFilters = ArrayUtils.create(Type.CalendarContextFilter);
//获取到有几种不同的筛选器 Obtain the unique filters.
for (var i = 0; i < toBeModifiedEventsID.length; i++) {
var event = CalendarIntegration_1.getCalendarEventById(toBeModifiedEventsID[i]);
toBeModifiedEvents.push(event);
// 获取当前事件的筛选器数组 Obtain the array of filters of the current event.
var filters = event.getContextFilters();
if (filters === undefined) {
continue;
}
console.log(filters);
// 遍历每个筛选器 Traverse each filter.
for (var j = 0; j < filters.length; j++) {
var filter = filters[j];
if (!uniqueFilters.includes(filter)) {
uniqueFilters.push(filter);
}
}
}
//如果找不到任何筛选器,则报错。If no filter is found, an error will be reported.
if (uniqueFilters.length === 0) {
Application.showMessage(ApplicationMessageType.Warning, “No context was found.”);
Application.hideBusyIndicator();
return;
} 4. Retrieve the dimension IDs under each model ID and the mapping between model IDs and model names. • From the deduplicated-filter array built in the previous step, extract every model ID together with its associated dimension IDs• For each model ID, obtain the corresponding datasource (via a hidden table) to read the model’s display name. var uniqueModelId = ArrayUtils.create(Type.string);
for (var m = 0; m < uniqueFilters.length; m++) {
var modelId_ = uniqueFilters[m].members[0].modelId;
var dimensionId = uniqueFilters[m].members[0].dimensionId;
if (!(uniqueModelDimensionId_1.includes(modelId_) && uniqueModelDimensionId_2.includes(dimensionId))) {
uniqueModelDimensionId_1.push(modelId_);
uniqueModelDimensionId_2.push(dimensionId);
}
if (!uniqueModelId.includes(modelId_)) {
uniqueModelId.push(modelId_);
}
}
var temp1 = ArrayUtils.create(Type.string);
var temp2 = ArrayUtils.create(Type.string);
for (var n = 0; n < uniqueModelId.length; n++) {
var modelId__ = uniqueModelId[n];
Table_1.setModel(modelId__);
datasource = Table_1.getDataSource();
temp1.push(modelId__);
temp2.push(datasource.getInfo().modelName);
}
for (var o = 0; o < temp1.length; o++) {
RadioButtonGroup_1.addItem(temp1[o], temp2[o]);
} 5. Once the user selects a model, retrieve the mapping between dimension IDs and their descriptions. • Match the dimension IDs returned by the Calendar API with those in the model’s datasource to obtain the description for each dimension. modelId = RadioButtonGroup_1.getSelectedKey();
datasource = Table_1.getDataSource();
var dimensions = datasource.getDimensions();
var descriptions = ArrayUtils.create(Type.string);
for (var h = 0; h < uniqueModelDimensionId_1.length; h++) {
if (uniqueModelDimensionId_1[h] === modelId) {
for (var j = 0; j < dimensions.length; j++) {
if (dimensions[j].id === uniqueModelDimensionId_2[h]) {
descriptions.push(dimensions[j].description);
break;
}
}
console.log(“error, dimension id dismatch”);
} else {
descriptions.push(“1”); //占位
}
}
for (var i = 0; i < uniqueModelDimensionId_1.length; i++) {
if (uniqueModelDimensionId_1[i] === modelId) {
CheckboxGroup_2.addItem(uniqueModelDimensionId_2[i], uniqueModelDimensionId_2[i] + ” ” + descriptions[i]);
}
} 6. After the user selects a dimension, retrieve the specific filters that need to be modified based on the chosen dimension ID. var selectedDimensions = CheckboxGroup_2.getSelectedKeys();
selectedFilters = ArrayUtils.create(Type.CalendarContextFilter);
for (var k = 0; k < toBeModifiedEvents.length; k++) {
var filters = toBeModifiedEvents[k].getContextFilters();
for (var m = 0; m < filters.length; m++) {
if (selectedDimensions.includes(filters[m].members[0].dimensionId) && filters[m].members[0].modelId === modelId && !selectedFilters.includes(filters[m])){
selectedFilters.push(filters[m]);
}
}
} 7. Add the filters earmarked for modification to the datasource so they can appear in the Filter Line. • Because the Version dimension is applied as a default filter on the datasource, pre-emptively place Version in the Table’s column axis to prevent this. var dimensions = datasource.getDimensions();
for (var s = 0; s < dimensions.length; s++) {
if (dimensions[s] == “Version”) {
Table_1.addDimensionToColumns(“Version”);
break;
}
}
for (var o = 0; o < selectedDimensions.length; o++) {
for (var u = 0; u < selectedFilters.length; u++) {
if (selectedFilters[u].members[0].dimensionId === selectedDimensions[o]) {
datasource.setDimensionFilter(selectedDimensions[o], selectedFilters[u].members);
}
}
} 8、emove old filters and add new ones.• First, deduplicate the previously stored array of filters that need modification.• Remove the old filters.• Construct the new Context Filter.• Add the new filter.• Append an entry to the log. var dimensions = datasource.getDimensions();
var selectedFiltersNoRepeat = ArrayUtils.create(Type.CalendarContextFilter);
for (var v = 0; v < selectedFilters.length; v++) {
if (!selectedFiltersNoRepeat.includes(selectedFilters[v])) {
selectedFiltersNoRepeat.push(selectedFilters[v]);
}
}
//selectedFilters 去重 Remove duplicates from selected filters.
for (var q = 0; q < selectedFiltersNoRepeat.length; q++) {
//selectedFilter是目前需要修改的筛选器 selectedFilter is the filter that currently needs to be modified.
var selectedFilter = selectedFiltersNoRepeat[q];
//复制一份变量,避免出现意外情况 Make a copy of the variable to avoid unexpected situations
var modifiedFilter = selectedFilter;
var dimensionId = modifiedFilter.members[0].dimensionId;
var newMemberInfo = ArrayUtils.create(Type.MemberInfo);
//构造一个原有members的字符串,以供日志使用 Construct a string of the original members for logging purposes.
var oldMembers = “”;
for (var r = 0; r < modifiedFilter.members.length; r++) {
oldMembers = oldMembers + ” ” + modifiedFilter.members[r].description;
}
//从datasource中获取到用户已经在filter line中修改好的filter Obtain the filter that the user has already modified in the filter line from the datasource.
var unspecifiedFilterValues = datasource.getDimensionFilters(dimensionId);
//删除旧的筛选器。remove old filter
var recordModifiedEvents = ArrayUtils.create(Type.integer);
for (var j = 0; j < toBeModifiedEvents.length; j++) {
if (toBeModifiedEvents[j].removeContextFilter(selectedFilter)) {
recordModifiedEvents.push(j);
}
}
//构建新的member info Construct the new member info.
for (var m = 0; m < unspecifiedFilterValues.length; m++) {
if (unspecifiedFilterValues[m].type == “Single”) {
var filterValue = cast(Type.SingleFilterValue, unspecifiedFilterValues[m]);
var thisMemberInfo = datasource.getMember(dimensionId, filterValue.value);
newMemberInfo.push(thisMemberInfo);
}
if (unspecifiedFilterValues[m].type == “Multiple”) {
var multipleFilterValue = cast(Type.MultipleFilterValue, unspecifiedFilterValues[m]);
var tempValues = multipleFilterValue.values;
for (var u = 0; u < tempValues.length; u++) {
var thisMemberInfo2 = datasource.getMember(dimensionId, tempValues[u]);
newMemberInfo.push(thisMemberInfo2);
}
}
}
//替换掉原来的member info Replace the original member info.
modifiedFilter.members = newMemberInfo;
//构造一个新members的字符串,以供日志使用 Construct a new string of members for logging purposes.
var newMembers = “”;
for (var e = 0; e < modifiedFilter.members.length; e++) {
newMembers = newMembers + ” ” + modifiedFilter.members[e].description;
}
//增加新的filter,并添加日志 Add a new filter and log the action.
for (var k = 0; k < recordModifiedEvents.length; k++) {
toBeModifiedEvents[recordModifiedEvents[k]].addContextFilter(modifiedFilter);
ListBox_1.addItem(toBeModifiedEvents[recordModifiedEvents[k]].getName()+” [“+modifiedFilter.members[0].dimensionId + “] ” + oldMembers + ” ->” + newMembers);
}
} 5. Conclusion This article shares the experience I gained while developing the “Bulk Edit Calendar Event Context” story application. If you’re facing similar challenges, I hope it sparks some ideas. Read More Technology Blog Posts by SAP articles
#SAP
#SAPTechnologyblog