14 KiB
Calendar, Bookmarks & Advanced Integration API Reference
Complete reference for Calendar integration, Bookmarks, Linked Analysis, and Timer APIs in SAP Analytics Cloud.
Source: Analytics Designer API Reference 2025.14
Table of Contents
Calendar Integration API
SAC Calendar allows integration with planning workflows, tasks, and processes.
Getting Calendar Events
getCalendarEventById(eventId)
Retrieves an existing calendar event by ID.
var event = Calendar.getCalendarEventById("EVENT_ID_STRING");
Note: The ID is a unique string (like a story/model ID), not the display name.
getCurrentEvent()
Returns the event from which the application was started.
var currentEvent = Calendar.getCurrentEvent();
if (currentEvent) {
console.log("Started from event:", currentEvent.getName());
}
Calendar Event Methods
Once you have an event reference, these methods are available:
| Method | Description |
|---|---|
getId() |
Returns event unique ID |
getName() |
Returns event display name |
getDescription() |
Returns event description |
getStartDate() |
Returns start date |
getDueDate() |
Returns due date |
getStatus() |
Returns current status |
getType() |
Returns event type |
getProgress() |
Returns completion percentage |
hasUserRole(role) |
Checks if user has specific role |
activate() |
Activates the event |
Calendar Composite Task
For composite tasks (multi-step workflows):
CalendarCompositeTask Methods
// Approval workflow
task.approve(); // Approve task (requires Reviewer role)
task.reject(); // Reject task
task.submit(); // Submit for review
task.decline(); // Decline task
// Check permissions
task.canUserApprove();
task.canUserReject();
task.canUserSubmit();
task.canUserDecline();
// Reviewer management
task.addReviewer(userId);
task.removeReviewer(userId);
Creating Calendar Events
Create Process
var processProperties = {
name: "Q4 Planning",
startDate: new Date(2024, 9, 1), // October 1, 2024
endDate: new Date(2024, 11, 31), // December 31, 2024
description: "Q4 Budget Planning Process",
owners: ["user1@company.com"],
assignees: ["user2@company.com", "user3@company.com"]
};
var newProcess = Calendar.createProcess(processProperties);
Create Task
var taskProperties = {
name: "Review Budget",
startDate: new Date(2024, 10, 1),
dueDate: new Date(2024, 10, 15),
description: "Review Q4 budget allocations",
assignees: ["reviewer@company.com"],
parentId: parentProcessId,
reminders: [
{ daysBeforeDue: 3 },
{ daysBeforeDue: 1 }
]
};
var newTask = Calendar.createTask(taskProperties);
CalendarCompositeTaskCreateProperties
Full list of properties when creating tasks:
| Property | Type | Description |
|---|---|---|
| name | string | Task name (required) |
| startDate | Date | Start date (required) |
| endDate | Date | End date (required) |
| dueDate | Date | Due date |
| description | string | Task description |
| owners | string[] | Owner user IDs |
| assignees | string[] | Assignee user IDs |
| reviewers | string[] | Reviewer user IDs |
| parentId | string | Parent process ID |
| dependencies | string[] | Dependent task IDs |
| contextFilters | object | Context filter settings |
| reminders | object[] | Reminder configurations |
| workFiles | string[] | Attached work file IDs |
Calendar Event Status
CalendarTaskStatus.Open // Not started
CalendarTaskStatus.InProgress // In progress
CalendarTaskStatus.Successful // Completed successfully
CalendarTaskStatus.Failed // Failed
CalendarTaskStatus.Canceled // Canceled
Reminder Configuration
// Add reminder to event
event.addReminder({
daysBeforeDue: 5,
notificationType: "Email"
});
Bulk Operations Example
// Bulk modify context filters on calendar events
function updateEventFilters(eventIds, newFilter) {
eventIds.forEach(function(eventId) {
var event = Calendar.getCalendarEventById(eventId);
if (event) {
event.setContextFilter("Year", newFilter);
}
});
}
Bookmarks API
Bookmarks save application state for later restoration.
What Bookmarks Save
- Dimension and measure selections in Tables/Charts
- Filter states
- Hierarchical levels
- Dropdown/Radio button/Checkbox selections
- Script Variables (primitive types: string, boolean, integer, number)
- Widget visibility states
- Sorting configurations
Not Saved: Non-primitive type script variables (complex objects)
BookmarkSet Technical Object
Add a BookmarkSet technical object to enable bookmark functionality.
saveBookmark(options)
Saves current application state as a bookmark.
BookmarkSet_1.saveBookmark({
name: "Q4 Analysis View",
isGlobal: true // false for personal bookmark
});
getAll()
Returns all available bookmarks.
var bookmarks = BookmarkSet_1.getAll();
bookmarks.forEach(function(bookmark) {
console.log(bookmark.id + ": " + bookmark.name);
});
apply(bookmarkId)
Applies a saved bookmark.
// Apply by ID
Bookmarks.apply("22305278-9717-4296-8809-298841349359");
// Or from bookmark info
var selectedBookmark = Dropdown_Bookmarks.getSelectedKey();
BookmarkSet_1.apply(selectedBookmark);
getAppliedBookmark()
Returns currently applied bookmark info.
var currentBookmark = BookmarkSet_1.getAppliedBookmark();
if (currentBookmark) {
console.log("Current bookmark:", currentBookmark.name);
}
deleteBookmark(bookmarkInfo)
Deletes a bookmark.
var bookmarkToDelete = BookmarkSet_1.getAppliedBookmark();
BookmarkSet_1.deleteBookmark(bookmarkToDelete);
URL Parameters for Bookmarks
Bookmarks can be loaded via URL:
# Load specific bookmark
?bookmarkId=XXXXXXXXXXX
# Load default bookmark
?bookmarkId=DEFAULT
Bookmark Dropdown Pattern
// Populate dropdown with bookmarks
function populateBookmarkDropdown() {
var bookmarks = BookmarkSet_1.getAll();
var items = bookmarks.map(function(b) {
return { key: b.id, text: b.name };
});
Dropdown_Bookmarks.setItems(items);
}
// Apply selected bookmark
Dropdown_Bookmarks.onSelect = function() {
var bookmarkId = Dropdown_Bookmarks.getSelectedKey();
if (bookmarkId) {
BookmarkSet_1.apply(bookmarkId);
}
};
Global vs Personal Bookmarks
| Type | Visibility | Use Case |
|---|---|---|
| Personal | Only creator | Individual analysis views |
| Global | All viewers | Shared scenarios, published views |
Linked Analysis API
Linked Analysis enables cross-widget filtering based on selections.
In Analytics Applications
Linked Analysis must be implemented via scripting in Analytic Applications (unlike Stories where it's automatic).
setFilters(selections)
Applies selection filters to linked widgets.
// In Chart_1 onSelect event
var selections = Chart_1.getSelections();
if (selections.length > 0) {
// Set filters on linked widgets
LinkedAnalysis.setFilters(selections);
}
removeFilters()
Removes linked analysis filters.
LinkedAnalysis.removeFilters();
Custom Linked Analysis Pattern
// Create reusable linked analysis function
function applyLinkedAnalysis(sourceWidget, targetWidgets) {
var selections = sourceWidget.getSelections();
if (selections.length === 0) {
// Clear filters on all targets
targetWidgets.forEach(function(widget) {
widget.getDataSource().clearAllFilters();
});
return;
}
// Apply selection to all targets
var selection = selections[0];
targetWidgets.forEach(function(widget) {
var ds = widget.getDataSource();
Object.keys(selection).forEach(function(dimId) {
ds.setDimensionFilter(dimId, selection[dimId]);
});
});
}
// Usage in Chart_1.onSelect
applyLinkedAnalysis(Chart_1, [Table_1, Chart_2, Chart_3]);
With Custom Widgets
Starting from QRC Q3 2023, Custom Widgets support Linked Analysis:
// In custom widget code
this.dataBindings
.getDataBinding()
.getLinkedAnalysis()
.setFilters(selection);
// Remove filters
this.dataBindings
.getDataBinding()
.getLinkedAnalysis()
.removeFilters();
Timer API
Timer enables scheduled script execution and animations.
Timer Technical Object
Add a Timer from the Outline panel under Technical Objects.
Timer Methods
// Start timer (interval in milliseconds)
Timer_1.start(1000); // Every 1 second
// Stop timer
Timer_1.stop();
// Check if running
var isRunning = Timer_1.isRunning();
// Get interval
var interval = Timer_1.getInterval();
// Set interval
Timer_1.setInterval(2000); // Change to 2 seconds
Timer Events
// onTimeout - fires at each interval
Timer_1.onTimeout = function() {
// Update data or animations
refreshDashboard();
};
Auto-Refresh Pattern
// Auto-refresh dashboard every 30 seconds
Timer_Refresh.start(30000);
Timer_Refresh.onTimeout = function() {
Application.showBusyIndicator();
Chart_1.getDataSource().refreshData();
Table_1.getDataSource().refreshData();
Application.hideBusyIndicator();
// Update timestamp
Text_LastRefresh.setText(
"Last updated: " + new Date().toLocaleTimeString()
);
};
// Stop on user interaction
Button_Pause.onClick = function() {
if (Timer_Refresh.isRunning()) {
Timer_Refresh.stop();
Button_Pause.setText("Resume Auto-Refresh");
} else {
Timer_Refresh.start(30000);
Button_Pause.setText("Pause Auto-Refresh");
}
};
Countdown Timer Pattern
var countdown = 60; // 60 seconds
Timer_Countdown.start(1000); // 1 second intervals
Timer_Countdown.onTimeout = function() {
countdown--;
Text_Countdown.setText("Time remaining: " + countdown + "s");
if (countdown <= 0) {
Timer_Countdown.stop();
performAction();
}
};
Animation Pattern
var currentIndex = 0;
var dataPoints = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"];
Timer_Animation.start(2000); // 2 second intervals
Timer_Animation.onTimeout = function() {
// Highlight next data point
Chart_1.getDataSource().setDimensionFilter("Month", dataPoints[currentIndex]);
currentIndex++;
if (currentIndex >= dataPoints.length) {
currentIndex = 0; // Loop back
}
};
Notification Badges
Application Messages
// Display notifications
Application.showMessage(ApplicationMessageType.Info, "Information message");
Application.showMessage(ApplicationMessageType.Success, "Success message");
Application.showMessage(ApplicationMessageType.Warning, "Warning message");
Application.showMessage(ApplicationMessageType.Error, "Error message");
Custom Notification Pattern
// Check for alerts and show notification
function checkAlerts() {
var data = Chart_1.getDataSource().getData({
"@MeasureDimension": "[Account].[parentId].&[Revenue]"
});
if (data.rawValue < 10000) {
Application.showMessage(
ApplicationMessageType.Warning,
"Revenue below threshold: " + data.formattedValue
);
// Open alert popup
Popup_Alert.open();
}
}
// Call on data change
Chart_1.onResultChanged = function() {
checkAlerts();
};
Tooltip Patterns
Tooltips appear automatically on chart data points. For custom tooltips:
// Use popup as custom tooltip
Chart_1.onSelect = function() {
var selections = Chart_1.getSelections();
if (selections.length > 0) {
// Get detailed data
var details = getDetailedInfo(selections[0]);
// Update tooltip content
Text_TooltipContent.setText(details);
// Position and show popup
Popup_Tooltip.open();
}
};
Complete Example: Planning Workflow with Calendar
// Initialize from calendar event
Application.onInitialization = function() {
var event = Calendar.getCurrentEvent();
if (event) {
// Set context from calendar event
var year = event.getContextFilter("Year");
var version = event.getContextFilter("Version");
if (year) {
Table_1.getDataSource().setDimensionFilter("Year", year);
}
if (version) {
Table_1.getDataSource().setDimensionFilter("Version", version);
}
// Update title
Text_Title.setText("Planning: " + event.getName());
// Show task info
Text_DueDate.setText("Due: " + event.getDueDate().toLocaleDateString());
}
};
// Submit task
Button_Submit.onClick = function() {
var event = Calendar.getCurrentEvent();
if (event && event.canUserSubmit()) {
// Save data first
Table_1.getPlanning().submitData();
// Submit for review
event.submit();
Application.showMessage(
ApplicationMessageType.Success,
"Task submitted for review"
);
} else {
Application.showMessage(
ApplicationMessageType.Warning,
"Cannot submit task"
);
}
};
Related Documentation
Official Reference: https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html