Files
gh-secondsky-sap-skills-ski…/references/api-calendar-bookmarks.md
2025-11-30 08:55:33 +08:00

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

  1. Calendar Integration API
  2. Bookmarks API
  3. Linked Analysis API
  4. Timer API
  5. Notification Badges

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"
        );
    }
};

Official Reference: https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html