Initial commit
This commit is contained in:
52
references/analytics-designer-restrictions.md
Normal file
52
references/analytics-designer-restrictions.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Analytics Designer Restrictions (2025.23)
|
||||
|
||||
Source: `help-portal-5128c44bafd04e19a4e1ee352ee1f14e.md` (Known Restrictions in Analytics Designer).
|
||||
|
||||
## Popup limitations
|
||||
- Popups larger than main canvas render poorly; add ≥2 widgets for proper display.
|
||||
- Height/width settings fail with a single widget.
|
||||
- Filter line widgets in popups lose source reference after reload; click each popup to reactivate.
|
||||
- `setTheme()` does not affect popups directly; place widgets in a panel and set theme there.
|
||||
- New theme settings require previewing each popup during design.
|
||||
- `enter`/`backspace` keys fail on multiple tables with Optimized Presentation; avoid that option.
|
||||
- Planning mass entry, distribute values, value locks, version history unsupported in popups—place table on canvas/panel and toggle visibility.
|
||||
|
||||
## Model API limits
|
||||
- `PlanningModel.createMembers()` works only for generic dimensions (not Date).
|
||||
- Version management APIs unsupported for BPC writeback-enabled versions; use UI publish/version tools instead.
|
||||
|
||||
## DataSource function limits
|
||||
- `setVariableValue`: no runtime/design-time validation; unsupported combos can error.
|
||||
- `setDimensionFilter`: ranges on fiscal hierarchies unsupported; users may still change filters in tables even if selection mode locked; runtime filter selection follows design-time selection mode.
|
||||
|
||||
## Smart Discovery
|
||||
- Cannot run after SmartDiscoveryAPIs without specifying Entity (pre-2021.1 versions).
|
||||
|
||||
## Styling limits
|
||||
- Explorer quick menus not controlled via styling panel (Filter/Exclude, Drill, Smart Insight remain).
|
||||
- Tables: “Restore to Theme Preference” fails after styling changes; default theme not reapplied.
|
||||
|
||||
## Export limits
|
||||
- Cannot save a theme as new after deletion.
|
||||
- Navigation panel: changing sort order unsupported for SAC models on SAP HANA.
|
||||
- PDF export: garbled text possible for CJK/Russian/Arabic; CSS partially applied; custom widgets may be incomplete; only visible widgets in containers export (no scrolled content).
|
||||
- Number format APIs do not affect tooltip measures (only axis measures).
|
||||
|
||||
## Chart API limits
|
||||
- `addMeasure`/`removeMeasure` feeds supported only: `Feed.ValueAxis`, `Feed.ValueAxis2`, `Feed.Color`, `Feed.bubbleWidth`.
|
||||
- `onSelect` event unsupported for cluster bubble charts.
|
||||
|
||||
## Browser limits
|
||||
- Safari: R visualizations with HTML blocked when 3rd-party cookies blocked; Request Desktop Website not supported for analytic apps on mobile Safari.
|
||||
|
||||
## Embedding
|
||||
- Embedding analytic apps into stories/digital boardroom via web page widget not officially supported.
|
||||
|
||||
## Input control limits
|
||||
- Only dimension member input controls; calculation input controls limited to restricted measures.
|
||||
- Dynamic forecast: calculation input controls for version/cut-over date in forecast tables unsupported.
|
||||
- Display: after `setSelectedMembers()` control may show member ID when collapsed/all members selected/hierarchy flat.
|
||||
|
||||
## Implementation notes
|
||||
- Use provided workarounds where possible; verify restricted features in target browser/device.
|
||||
|
||||
793
references/api-advanced-widgets.md
Normal file
793
references/api-advanced-widgets.md
Normal file
@@ -0,0 +1,793 @@
|
||||
# Advanced Widgets & Layout API Reference
|
||||
|
||||
Complete reference for Container Widgets, Layout APIs, R Visualizations, Custom Widgets, and Navigation in SAP Analytics Cloud.
|
||||
|
||||
**Source**: [Analytics Designer API Reference 2025.14](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Container Widgets](#container-widgets)
|
||||
2. [Layout API](#layout-api)
|
||||
3. [Navigation API](#navigation-api)
|
||||
4. [R Visualization](#r-visualization)
|
||||
5. [Custom Widgets](#custom-widgets)
|
||||
6. [Script Objects](#script-objects)
|
||||
7. [Technical Objects](#technical-objects)
|
||||
|
||||
---
|
||||
|
||||
## Container Widgets
|
||||
|
||||
SAC provides five container widget types for organizing content.
|
||||
|
||||
### Panel
|
||||
|
||||
Basic container for grouping widgets.
|
||||
|
||||
```javascript
|
||||
// Show/hide panel
|
||||
Panel_1.setVisible(true);
|
||||
Panel_1.setVisible(false);
|
||||
|
||||
// Check visibility
|
||||
var isVisible = Panel_1.isVisible();
|
||||
```
|
||||
|
||||
**Characteristics**:
|
||||
- Groups widgets together
|
||||
- Move, copy, delete affects all contained widgets
|
||||
- No navigation functionality
|
||||
- Cannot execute scripts on panel click directly
|
||||
|
||||
### TabStrip
|
||||
|
||||
Container with tabbed navigation.
|
||||
|
||||
```javascript
|
||||
// Get active tab
|
||||
var activeTab = TabStrip_1.getSelectedTab();
|
||||
|
||||
// Set active tab
|
||||
TabStrip_1.setSelectedTab("Tab_Sales");
|
||||
|
||||
// Get all tabs
|
||||
var tabs = TabStrip_1.getTabs();
|
||||
```
|
||||
|
||||
**Events**:
|
||||
```javascript
|
||||
TabStrip_1.onSelect = function() {
|
||||
var selectedTab = TabStrip_1.getSelectedTab();
|
||||
console.log("Switched to tab:", selectedTab);
|
||||
|
||||
// Refresh data for newly visible tab
|
||||
if (selectedTab === "Tab_Details") {
|
||||
Table_Details.getDataSource().refreshData();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### PageBook
|
||||
|
||||
Container for page-based navigation.
|
||||
|
||||
```javascript
|
||||
// Navigate to page
|
||||
PageBook_1.setSelectedPage("Page_Dashboard");
|
||||
|
||||
// Get current page
|
||||
var currentPage = PageBook_1.getSelectedPage();
|
||||
|
||||
// Navigate by index
|
||||
PageBook_1.setSelectedPageIndex(0); // First page
|
||||
```
|
||||
|
||||
**Page Navigation Pattern**:
|
||||
```javascript
|
||||
// Navigation buttons
|
||||
Button_Next.onClick = function() {
|
||||
var pages = PageBook_1.getPages();
|
||||
var currentIndex = pages.indexOf(PageBook_1.getSelectedPage());
|
||||
|
||||
if (currentIndex < pages.length - 1) {
|
||||
PageBook_1.setSelectedPageIndex(currentIndex + 1);
|
||||
}
|
||||
};
|
||||
|
||||
Button_Previous.onClick = function() {
|
||||
var pages = PageBook_1.getPages();
|
||||
var currentIndex = pages.indexOf(PageBook_1.getSelectedPage());
|
||||
|
||||
if (currentIndex > 0) {
|
||||
PageBook_1.setSelectedPageIndex(currentIndex - 1);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Flow Layout Panel
|
||||
|
||||
Responsive container with automatic reflow.
|
||||
|
||||
```javascript
|
||||
// Same visibility controls as Panel
|
||||
FlowLayoutPanel_1.setVisible(true);
|
||||
```
|
||||
|
||||
**Characteristics**:
|
||||
- Widgets placed sequentially (not freely positioned)
|
||||
- Automatic reflow on resize
|
||||
- Responsive behavior (widgets wrap to next row)
|
||||
- Similar to responsive lanes
|
||||
|
||||
**Use Case**: Create responsive layouts that adapt to screen size.
|
||||
|
||||
### Popup
|
||||
|
||||
Overlay container that appears above content.
|
||||
|
||||
```javascript
|
||||
// Open popup
|
||||
Popup_1.open();
|
||||
|
||||
// Close popup
|
||||
Popup_1.close();
|
||||
```
|
||||
|
||||
**Events**:
|
||||
```javascript
|
||||
Popup_1.onOpen = function() {
|
||||
// Initialize popup content
|
||||
refreshPopupData();
|
||||
};
|
||||
|
||||
Popup_1.onClose = function() {
|
||||
// Cleanup
|
||||
clearPopupSelections();
|
||||
};
|
||||
```
|
||||
|
||||
**Dialog Mode**: Enable "Header & Footer" in Builder for dialog style with title bar and buttons.
|
||||
|
||||
---
|
||||
|
||||
## Layout API
|
||||
|
||||
Dynamically control widget size and position.
|
||||
|
||||
### Position Methods
|
||||
|
||||
```javascript
|
||||
// Get position (pixels from edge)
|
||||
var left = Widget_1.getLeft();
|
||||
var top = Widget_1.getTop();
|
||||
var right = Widget_1.getRight();
|
||||
var bottom = Widget_1.getBottom();
|
||||
|
||||
// Set position
|
||||
Widget_1.setLeft(100);
|
||||
Widget_1.setTop(50);
|
||||
Widget_1.setRight(200);
|
||||
Widget_1.setBottom(100);
|
||||
```
|
||||
|
||||
### Size Methods
|
||||
|
||||
```javascript
|
||||
// Get dimensions
|
||||
var width = Widget_1.getWidth();
|
||||
var height = Widget_1.getHeight();
|
||||
|
||||
// Set dimensions (pixels or percentage)
|
||||
Widget_1.setWidth(500); // 500 pixels
|
||||
Widget_1.setHeight(300); // 300 pixels
|
||||
|
||||
// Percentage-based
|
||||
Widget_1.setWidth("50%");
|
||||
Widget_1.setHeight("auto");
|
||||
```
|
||||
|
||||
### Responsive Layout Pattern
|
||||
|
||||
```javascript
|
||||
Application.onResize = function(windowWidth, windowHeight) {
|
||||
// Mobile layout
|
||||
if (windowWidth < 768) {
|
||||
Chart_1.setWidth("100%");
|
||||
Chart_1.setHeight(200);
|
||||
Table_1.setWidth("100%");
|
||||
Panel_Sidebar.setVisible(false);
|
||||
}
|
||||
// Tablet layout
|
||||
else if (windowWidth < 1024) {
|
||||
Chart_1.setWidth("50%");
|
||||
Chart_1.setHeight(300);
|
||||
Table_1.setWidth("50%");
|
||||
Panel_Sidebar.setVisible(true);
|
||||
}
|
||||
// Desktop layout
|
||||
else {
|
||||
Chart_1.setWidth("70%");
|
||||
Chart_1.setHeight(400);
|
||||
Table_1.setWidth("30%");
|
||||
Panel_Sidebar.setVisible(true);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Collapsible Panel Pattern
|
||||
|
||||
```javascript
|
||||
var isPanelExpanded = true;
|
||||
|
||||
Button_TogglePanel.onClick = function() {
|
||||
isPanelExpanded = !isPanelExpanded;
|
||||
|
||||
if (isPanelExpanded) {
|
||||
Panel_Navigation.setWidth(250);
|
||||
Panel_Content.setLeft(260);
|
||||
Button_TogglePanel.setText("◀");
|
||||
} else {
|
||||
Panel_Navigation.setWidth(50);
|
||||
Panel_Content.setLeft(60);
|
||||
Button_TogglePanel.setText("▶");
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Navigation API
|
||||
|
||||
Navigate between applications and stories.
|
||||
|
||||
### NavigationUtils
|
||||
|
||||
```javascript
|
||||
// Open another application
|
||||
NavigationUtils.openApplication("APP_ID", {
|
||||
mode: ApplicationMode.View,
|
||||
newWindow: true
|
||||
});
|
||||
|
||||
// Open a story
|
||||
NavigationUtils.openStory("STORY_ID", {
|
||||
newWindow: false
|
||||
});
|
||||
|
||||
// Open URL
|
||||
NavigationUtils.openUrl("[https://example.com",](https://example.com",) true); // true = new window
|
||||
```
|
||||
|
||||
### Create Application URL with Parameters
|
||||
|
||||
```javascript
|
||||
var url = NavigationUtils.createApplicationUrl("APP_ID", {
|
||||
p_year: "2024",
|
||||
p_region: "EMEA",
|
||||
bookmarkId: "DEFAULT"
|
||||
});
|
||||
|
||||
console.log("Share URL:", url);
|
||||
```
|
||||
|
||||
### Page Navigation (Within Application)
|
||||
|
||||
For applications with multiple pages:
|
||||
|
||||
```javascript
|
||||
// Switch to page
|
||||
Application.setActivePage("Page_Details");
|
||||
|
||||
// Get current page
|
||||
var currentPage = Application.getActivePage();
|
||||
```
|
||||
|
||||
### Custom Navigation Menu Pattern
|
||||
|
||||
```javascript
|
||||
// Navigation sidebar buttons
|
||||
Button_Dashboard.onClick = function() {
|
||||
highlightNavButton(Button_Dashboard);
|
||||
PageBook_1.setSelectedPage("Page_Dashboard");
|
||||
};
|
||||
|
||||
Button_Analysis.onClick = function() {
|
||||
highlightNavButton(Button_Analysis);
|
||||
PageBook_1.setSelectedPage("Page_Analysis");
|
||||
};
|
||||
|
||||
Button_Settings.onClick = function() {
|
||||
highlightNavButton(Button_Settings);
|
||||
PageBook_1.setSelectedPage("Page_Settings");
|
||||
};
|
||||
|
||||
// Helper function
|
||||
function highlightNavButton(activeButton) {
|
||||
var navButtons = [Button_Dashboard, Button_Analysis, Button_Settings];
|
||||
navButtons.forEach(function(btn) {
|
||||
if (btn === activeButton) {
|
||||
btn.setCssClass("nav-button-active");
|
||||
} else {
|
||||
btn.setCssClass("nav-button");
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Cross-Story Navigation
|
||||
|
||||
```javascript
|
||||
// Navigate to story with filters
|
||||
Button_DrillToStory.onClick = function() {
|
||||
var selections = Table_1.getSelections();
|
||||
|
||||
if (selections.length > 0) {
|
||||
var productId = selections[0]["Product"];
|
||||
|
||||
NavigationUtils.openStory("DETAIL_STORY_ID", {
|
||||
newWindow: false,
|
||||
urlParameters: {
|
||||
p_product: productId,
|
||||
p_year: Dropdown_Year.getSelectedKey()
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## R Visualization
|
||||
|
||||
R widgets allow custom visualizations using R scripts.
|
||||
|
||||
### Adding R Visualization
|
||||
|
||||
1. Insert → R Visualization in Story
|
||||
2. Configure Input Data (data source binding)
|
||||
3. Write R script in Script Editor
|
||||
|
||||
### R Widget Configuration
|
||||
|
||||
**Input Data**: Binds SAC data to R variables
|
||||
|
||||
**R Script**: Executes R code on the input data
|
||||
|
||||
### Common R Libraries
|
||||
|
||||
```r
|
||||
# Available libraries include:
|
||||
library(ggplot2) # Data visualization
|
||||
library(plotly) # Interactive charts
|
||||
library(dplyr) # Data manipulation
|
||||
library(tidyr) # Data tidying
|
||||
library(scales) # Scale functions
|
||||
```
|
||||
|
||||
### Basic R Chart Example
|
||||
|
||||
```r
|
||||
# Data comes from SAC as data frame
|
||||
# 'data' is the input data variable
|
||||
|
||||
library(ggplot2)
|
||||
|
||||
# Create bar chart
|
||||
ggplot(data, aes(x=Category, y=Value, fill=Region)) +
|
||||
geom_bar(stat="identity", position="dodge") +
|
||||
theme_minimal() +
|
||||
labs(title="Sales by Category and Region")
|
||||
```
|
||||
|
||||
### Interactive Plotly Example
|
||||
|
||||
```r
|
||||
library(plotly)
|
||||
|
||||
# Create interactive chart
|
||||
plot_ly(data,
|
||||
x = ~Month,
|
||||
y = ~Revenue,
|
||||
type = 'scatter',
|
||||
mode = 'lines+markers',
|
||||
color = ~Product) %>%
|
||||
layout(title = "Monthly Revenue Trend")
|
||||
```
|
||||
|
||||
### Animated R Visualization
|
||||
|
||||
```r
|
||||
library(gganimate)
|
||||
library(ggplot2)
|
||||
|
||||
# Animated chart
|
||||
p <- ggplot(data, aes(x=Year, y=Value, color=Category)) +
|
||||
geom_point(size=5) +
|
||||
transition_time(Year) +
|
||||
labs(title = "Year: {frame_time}")
|
||||
|
||||
animate(p, nframes=50)
|
||||
```
|
||||
|
||||
### R Visualization Limitations
|
||||
|
||||
- No direct script interaction with SAC widgets
|
||||
- Input data must be pre-filtered via data source binding
|
||||
- Cannot call R scripts programmatically from SAC script
|
||||
|
||||
### Use Cases
|
||||
|
||||
- Specialized statistical visualizations
|
||||
- Animated charts
|
||||
- Complex data science visualizations
|
||||
- Charts not available in standard SAC library
|
||||
|
||||
---
|
||||
|
||||
## Custom Widgets
|
||||
|
||||
Extend SAC with custom web components.
|
||||
|
||||
### Custom Widget Structure
|
||||
|
||||
```
|
||||
my-widget/
|
||||
├── widget.json # Metadata and configuration
|
||||
├── widget.js # JavaScript implementation
|
||||
├── widget.css # Optional styling
|
||||
└── assets/ # Optional images/resources
|
||||
```
|
||||
|
||||
### widget.json Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "com.company.mywidget",
|
||||
"version": "1.0.0",
|
||||
"name": "My Custom Widget",
|
||||
"description": "A custom widget for SAC",
|
||||
"icon": "data:image/png;base64,...",
|
||||
"webComponent": {
|
||||
"tagName": "my-custom-widget",
|
||||
"entryPoint": "widget.js"
|
||||
},
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"default": "Widget Title"
|
||||
},
|
||||
"threshold": {
|
||||
"type": "number",
|
||||
"default": 100
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"refresh": {
|
||||
"returnType": "void",
|
||||
"description": "Refreshes the widget"
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"onClick": {
|
||||
"description": "Fires when widget is clicked"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### widget.js Structure
|
||||
|
||||
```javascript
|
||||
(function() {
|
||||
let template = document.createElement("template");
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
.container { padding: 10px; }
|
||||
.title { font-weight: bold; }
|
||||
</style>
|
||||
<div class="container">
|
||||
<div class="title"></div>
|
||||
<div class="content"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
class MyWidget extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: "open" });
|
||||
this.shadowRoot.appendChild(template.content.cloneNode(true));
|
||||
this._props = {};
|
||||
}
|
||||
|
||||
// Lifecycle: widget added to DOM
|
||||
connectedCallback() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
// Called when properties change
|
||||
onCustomWidgetBeforeUpdate(changedProps) {
|
||||
this._props = { ...this._props, ...changedProps };
|
||||
}
|
||||
|
||||
onCustomWidgetAfterUpdate(changedProps) {
|
||||
this.render();
|
||||
}
|
||||
|
||||
// Called when widget is resized
|
||||
onCustomWidgetResize(width, height) {
|
||||
// Handle resize
|
||||
}
|
||||
|
||||
// Called when widget is removed
|
||||
onCustomWidgetDestroy() {
|
||||
// Cleanup
|
||||
}
|
||||
|
||||
render() {
|
||||
let titleEl = this.shadowRoot.querySelector(".title");
|
||||
titleEl.textContent = this._props.title || "";
|
||||
}
|
||||
|
||||
// Custom method (callable from SAC script)
|
||||
refresh() {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("my-custom-widget", MyWidget);
|
||||
})();
|
||||
```
|
||||
|
||||
### Using Custom Widgets in SAC
|
||||
|
||||
```javascript
|
||||
// Access custom widget
|
||||
CustomWidget_1.setTitle("New Title");
|
||||
|
||||
// Call custom method
|
||||
CustomWidget_1.refresh();
|
||||
|
||||
// Handle custom event
|
||||
CustomWidget_1.onClick = function() {
|
||||
// Widget was clicked
|
||||
Application.showMessage(ApplicationMessageType.Info, "Widget clicked!");
|
||||
};
|
||||
```
|
||||
|
||||
### Custom Widget with Data Binding
|
||||
|
||||
```javascript
|
||||
// In custom widget (supports Linked Analysis)
|
||||
this.dataBindings
|
||||
.getDataBinding()
|
||||
.getLinkedAnalysis()
|
||||
.setFilters(selection);
|
||||
```
|
||||
|
||||
### Timer Custom Widget Example
|
||||
|
||||
```javascript
|
||||
// TimeCountdown widget pattern
|
||||
class TimeCountdownWidget extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this._endDate = new Date();
|
||||
this._timer = null;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.startCountdown();
|
||||
}
|
||||
|
||||
startCountdown() {
|
||||
this._timer = setInterval(() => {
|
||||
this.updateDisplay();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
updateDisplay() {
|
||||
const now = new Date();
|
||||
const diff = this._endDate - now;
|
||||
|
||||
if (diff <= 0) {
|
||||
clearInterval(this._timer);
|
||||
this.innerHTML = "Time's up!";
|
||||
return;
|
||||
}
|
||||
|
||||
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||||
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
|
||||
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
|
||||
|
||||
this.innerHTML = `${days}d ${hours}h ${minutes}m ${seconds}s`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Script Objects
|
||||
|
||||
Reusable function containers.
|
||||
|
||||
### Creating Script Objects
|
||||
|
||||
1. Outline panel → Script Objects → (+)
|
||||
2. Name the script object (e.g., `Utils`)
|
||||
3. Add functions
|
||||
|
||||
### Script Object Functions
|
||||
|
||||
```javascript
|
||||
// In ScriptObject: Utils
|
||||
|
||||
// Function: formatCurrency
|
||||
function formatCurrency(value, currency) {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: currency || 'USD'
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
// Function: getQuarter
|
||||
function getQuarter(date) {
|
||||
var month = date.getMonth();
|
||||
return Math.floor(month / 3) + 1;
|
||||
}
|
||||
|
||||
// Function: applyStandardFilters
|
||||
function applyStandardFilters(dataSource, year, region) {
|
||||
dataSource.setRefreshPaused(true);
|
||||
dataSource.setDimensionFilter("Year", year);
|
||||
dataSource.setDimensionFilter("Region", region);
|
||||
dataSource.setRefreshPaused(false);
|
||||
}
|
||||
```
|
||||
|
||||
### Calling Script Object Functions
|
||||
|
||||
```javascript
|
||||
// From any event handler
|
||||
var formatted = Utils.formatCurrency(1234.56, "EUR");
|
||||
// Returns: "€1,234.56"
|
||||
|
||||
var quarter = Utils.getQuarter(new Date());
|
||||
// Returns: 1-4
|
||||
|
||||
Utils.applyStandardFilters(
|
||||
Chart_1.getDataSource(),
|
||||
"2024",
|
||||
"EMEA"
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Technical Objects
|
||||
|
||||
Special objects for advanced functionality.
|
||||
|
||||
### Available Technical Objects
|
||||
|
||||
| Object | Description |
|
||||
|--------|-------------|
|
||||
| BookmarkSet | Save/load application state |
|
||||
| Timer | Scheduled script execution |
|
||||
| Calendar | Calendar integration |
|
||||
| DataAction | Execute data actions |
|
||||
| MultiAction | Execute multiple actions |
|
||||
| ScriptVariable | Application-wide variables |
|
||||
|
||||
### Adding Technical Objects
|
||||
|
||||
1. Outline panel → Technical Objects
|
||||
2. Click (+) next to desired object type
|
||||
3. Configure in Builder panel
|
||||
|
||||
### Global Script Variables
|
||||
|
||||
Create via Outline → Script Variables:
|
||||
|
||||
```javascript
|
||||
// Access global variable
|
||||
var currentYear = GlobalYear;
|
||||
|
||||
// Set global variable
|
||||
GlobalYear = "2024";
|
||||
|
||||
// URL parameter (prefix with p_)
|
||||
// URL: ?p_Year=2024
|
||||
// Variable name: p_Year
|
||||
var urlYear = p_Year;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Example: Custom Dashboard Layout
|
||||
|
||||
```javascript
|
||||
// Initialize responsive layout
|
||||
Application.onInitialization = function() {
|
||||
// Set initial layout based on window size
|
||||
adjustLayout(window.innerWidth);
|
||||
};
|
||||
|
||||
Application.onResize = function(width, height) {
|
||||
adjustLayout(width);
|
||||
};
|
||||
|
||||
function adjustLayout(width) {
|
||||
// Mobile
|
||||
if (width < 768) {
|
||||
setMobileLayout();
|
||||
}
|
||||
// Tablet
|
||||
else if (width < 1200) {
|
||||
setTabletLayout();
|
||||
}
|
||||
// Desktop
|
||||
else {
|
||||
setDesktopLayout();
|
||||
}
|
||||
}
|
||||
|
||||
function setMobileLayout() {
|
||||
Panel_Sidebar.setVisible(false);
|
||||
Panel_Main.setWidth("100%");
|
||||
Panel_Main.setLeft(0);
|
||||
|
||||
TabStrip_Charts.setSelectedTab("Tab_Summary");
|
||||
Chart_Detail.setVisible(false);
|
||||
}
|
||||
|
||||
function setTabletLayout() {
|
||||
Panel_Sidebar.setVisible(true);
|
||||
Panel_Sidebar.setWidth(200);
|
||||
Panel_Main.setWidth("calc(100% - 210px)");
|
||||
Panel_Main.setLeft(210);
|
||||
|
||||
Chart_Detail.setVisible(true);
|
||||
Chart_Detail.setWidth("100%");
|
||||
}
|
||||
|
||||
function setDesktopLayout() {
|
||||
Panel_Sidebar.setVisible(true);
|
||||
Panel_Sidebar.setWidth(250);
|
||||
Panel_Main.setWidth("calc(100% - 260px)");
|
||||
Panel_Main.setLeft(260);
|
||||
|
||||
Chart_Detail.setVisible(true);
|
||||
Chart_Detail.setWidth("50%");
|
||||
}
|
||||
|
||||
// Navigation
|
||||
Button_Dashboard.onClick = function() {
|
||||
PageBook_1.setSelectedPage("Page_Dashboard");
|
||||
Utils.highlightNavButton("Dashboard");
|
||||
};
|
||||
|
||||
Button_Analysis.onClick = function() {
|
||||
PageBook_1.setSelectedPage("Page_Analysis");
|
||||
Utils.highlightNavButton("Analysis");
|
||||
};
|
||||
|
||||
Button_Export.onClick = function() {
|
||||
Popup_Export.open();
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [DataSource API](api-datasource.md)
|
||||
- [Widgets API](api-widgets.md)
|
||||
- [Planning API](api-planning.md)
|
||||
- [Calendar & Bookmarks API](api-calendar-bookmarks.md)
|
||||
|
||||
**Official References**:
|
||||
- [Analytics Designer API Reference 2025.14](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
- [Custom Widget Developer Guide](https://help.sap.com/doc/c813a28922b54e50bd2a307b099787dc/release/en-US/CustomWidgetDevGuide_en.pdf)
|
||||
630
references/api-application.md
Normal file
630
references/api-application.md
Normal file
@@ -0,0 +1,630 @@
|
||||
# Application API Reference
|
||||
|
||||
Complete reference for the Application object and utility APIs in SAP Analytics Cloud scripting.
|
||||
|
||||
**Source**: [Analytics Designer API Reference 2025.14](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Application Object](#application-object)
|
||||
2. [User Feedback](#user-feedback)
|
||||
3. [Application Information](#application-information)
|
||||
4. [Navigation](#navigation)
|
||||
5. [Export Functions](#export-functions)
|
||||
6. [Theme and Styling](#theme-and-styling)
|
||||
7. [Application Events](#application-events)
|
||||
8. [Utility APIs](#utility-apis)
|
||||
9. [Enumerations](#enumerations)
|
||||
|
||||
---
|
||||
|
||||
## Application Object
|
||||
|
||||
The global `Application` object provides access to application-level functionality.
|
||||
|
||||
```javascript
|
||||
// Always available - no need to get reference
|
||||
Application.showBusyIndicator();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## User Feedback
|
||||
|
||||
### showBusyIndicator()
|
||||
|
||||
Shows loading indicator overlay.
|
||||
|
||||
```javascript
|
||||
Application.showBusyIndicator();
|
||||
|
||||
// Perform long operation
|
||||
await someAsyncOperation();
|
||||
|
||||
Application.hideBusyIndicator();
|
||||
```
|
||||
|
||||
### hideBusyIndicator()
|
||||
|
||||
Hides loading indicator.
|
||||
|
||||
```javascript
|
||||
Application.hideBusyIndicator();
|
||||
```
|
||||
|
||||
### showMessage(type, message)
|
||||
|
||||
Displays message to user.
|
||||
|
||||
**Parameters**:
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| type | ApplicationMessageType | Message type |
|
||||
| message | string | Message text |
|
||||
|
||||
```javascript
|
||||
// Info message
|
||||
Application.showMessage(ApplicationMessageType.Info, "Operation completed");
|
||||
|
||||
// Success message
|
||||
Application.showMessage(ApplicationMessageType.Success, "Data saved");
|
||||
|
||||
// Warning message
|
||||
Application.showMessage(ApplicationMessageType.Warning, "Unsaved changes");
|
||||
|
||||
// Error message
|
||||
Application.showMessage(ApplicationMessageType.Error, "Operation failed");
|
||||
```
|
||||
|
||||
### Confirm Dialog Pattern
|
||||
|
||||
```javascript
|
||||
// Use popup/dialog for confirmations
|
||||
// Create Popup_Confirm with Yes/No buttons
|
||||
|
||||
Button_Delete.onClick = function() {
|
||||
Popup_Confirm.open();
|
||||
};
|
||||
|
||||
Button_Yes.onClick = function() {
|
||||
// Perform action
|
||||
performDelete();
|
||||
Popup_Confirm.close();
|
||||
};
|
||||
|
||||
Button_No.onClick = function() {
|
||||
Popup_Confirm.close();
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Application Information
|
||||
|
||||
### getInfo()
|
||||
|
||||
Returns application metadata.
|
||||
|
||||
```javascript
|
||||
var info = Application.getInfo();
|
||||
// Returns: ApplicationInfo object
|
||||
// - id: string
|
||||
// - name: string
|
||||
// - description: string
|
||||
```
|
||||
|
||||
### getUserInfo()
|
||||
|
||||
Returns current user information.
|
||||
|
||||
```javascript
|
||||
var user = Application.getUserInfo();
|
||||
// Returns: UserInfo object
|
||||
// - id: string
|
||||
// - displayName: string
|
||||
// - email: string
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
var user = Application.getUserInfo();
|
||||
Text_Welcome.setText("Welcome, " + user.displayName);
|
||||
```
|
||||
|
||||
### getRolesInfo()
|
||||
|
||||
Returns user roles.
|
||||
|
||||
```javascript
|
||||
var roles = Application.getRolesInfo();
|
||||
```
|
||||
|
||||
### getMode()
|
||||
|
||||
Returns current application mode.
|
||||
|
||||
```javascript
|
||||
var mode = Application.getMode();
|
||||
// Returns: ApplicationMode enum value
|
||||
// - ApplicationMode.View
|
||||
// - ApplicationMode.Present
|
||||
// - ApplicationMode.Embed
|
||||
```
|
||||
|
||||
**Example: Conditional Display**:
|
||||
```javascript
|
||||
if (Application.getMode() === ApplicationMode.Present) {
|
||||
Button_Edit.setVisible(false);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Navigation
|
||||
|
||||
### NavigationUtils
|
||||
|
||||
Navigate to other applications or stories.
|
||||
|
||||
#### openApplication(appId, options)
|
||||
|
||||
Opens another analytic application.
|
||||
|
||||
```javascript
|
||||
NavigationUtils.openApplication("APP_ID", {
|
||||
mode: ApplicationMode.View,
|
||||
newWindow: true
|
||||
});
|
||||
```
|
||||
|
||||
#### openStory(storyId, options)
|
||||
|
||||
Opens a story.
|
||||
|
||||
```javascript
|
||||
NavigationUtils.openStory("STORY_ID", {
|
||||
newWindow: false
|
||||
});
|
||||
```
|
||||
|
||||
#### createApplicationUrl(appId, parameters)
|
||||
|
||||
Creates URL for an application with parameters.
|
||||
|
||||
```javascript
|
||||
var url = NavigationUtils.createApplicationUrl("APP_ID", {
|
||||
p_year: "2024",
|
||||
p_region: "EMEA"
|
||||
});
|
||||
// Returns: URL string with encoded parameters
|
||||
```
|
||||
|
||||
### Page Navigation (Within Application)
|
||||
|
||||
```javascript
|
||||
// Get active page
|
||||
var page = Application.getActivePage();
|
||||
|
||||
// Set active page
|
||||
Application.setActivePage("Page_Detail");
|
||||
```
|
||||
|
||||
### Tab Navigation
|
||||
|
||||
```javascript
|
||||
// Switch tab
|
||||
TabStrip_1.setSelectedTab("Tab_Chart");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Export Functions
|
||||
|
||||
### Export PDF
|
||||
|
||||
```javascript
|
||||
Application.export(ExportType.PDF, {
|
||||
scope: ExportScope.All,
|
||||
header: "Sales Report",
|
||||
footer: "Confidential - Page {page}",
|
||||
orientation: "landscape",
|
||||
paperSize: "A4"
|
||||
});
|
||||
```
|
||||
|
||||
### Export Excel
|
||||
|
||||
```javascript
|
||||
Application.export(ExportType.Excel, {
|
||||
scope: ExportScope.All,
|
||||
includeHierarchy: true,
|
||||
fileName: "SalesData"
|
||||
});
|
||||
```
|
||||
|
||||
### Export CSV (Widget Level)
|
||||
|
||||
```javascript
|
||||
Table_1.export(ExportType.CSV, {
|
||||
fileName: "TableExport"
|
||||
});
|
||||
```
|
||||
|
||||
### Export PowerPoint
|
||||
|
||||
```javascript
|
||||
Application.export(ExportType.PowerPoint, {
|
||||
scope: ExportScope.All,
|
||||
header: "Quarterly Review"
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Theme and Styling
|
||||
|
||||
### getTheme()
|
||||
|
||||
Returns current theme.
|
||||
|
||||
```javascript
|
||||
var theme = Application.getTheme();
|
||||
// Returns: "sap_fiori_3" | "sap_fiori_3_dark" | etc.
|
||||
```
|
||||
|
||||
### setTheme(themeId)
|
||||
|
||||
Sets application theme.
|
||||
|
||||
```javascript
|
||||
Application.setTheme("sap_fiori_3_dark");
|
||||
```
|
||||
|
||||
### getCssClass()
|
||||
|
||||
Returns application CSS class.
|
||||
|
||||
```javascript
|
||||
var cssClass = Application.getCssClass();
|
||||
```
|
||||
|
||||
### setCssClass(className)
|
||||
|
||||
Sets application CSS class.
|
||||
|
||||
```javascript
|
||||
Application.setCssClass("myCustomTheme");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Application Events
|
||||
|
||||
### onInitialization
|
||||
|
||||
Fires once when application loads.
|
||||
|
||||
```javascript
|
||||
Application.onInitialization = function() {
|
||||
// Initialize application state
|
||||
// BEST PRACTICE: Keep this empty for performance
|
||||
};
|
||||
```
|
||||
|
||||
**Best Practice**: Avoid heavy operations. Use:
|
||||
- URL parameters for initial values
|
||||
- Static widget filters
|
||||
- Background loading for hidden widgets
|
||||
|
||||
### onResize
|
||||
|
||||
Fires when application is resized.
|
||||
|
||||
```javascript
|
||||
Application.onResize = function(width, height) {
|
||||
console.log("New size:", width, height);
|
||||
|
||||
// Adjust layout
|
||||
if (width < 768) {
|
||||
Chart_1.setVisible(false);
|
||||
Table_1.setVisible(true);
|
||||
} else {
|
||||
Chart_1.setVisible(true);
|
||||
Table_1.setVisible(true);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### onOrientationChange
|
||||
|
||||
Fires on mobile when orientation changes.
|
||||
|
||||
```javascript
|
||||
Application.onOrientationChange = function(orientation) {
|
||||
console.log("Orientation:", orientation);
|
||||
// "portrait" | "landscape"
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Utility APIs
|
||||
|
||||
### ConvertUtils
|
||||
|
||||
Type conversion utilities.
|
||||
|
||||
```javascript
|
||||
// String to Integer
|
||||
var num = ConvertUtils.stringToInteger("42");
|
||||
// Returns: 42
|
||||
|
||||
// Number to String
|
||||
var str = ConvertUtils.numberToString(42);
|
||||
// Returns: "42"
|
||||
|
||||
// String to Number (with decimals)
|
||||
var decimal = ConvertUtils.stringToNumber("42.5");
|
||||
// Returns: 42.5
|
||||
```
|
||||
|
||||
### NumberFormat
|
||||
|
||||
Number formatting options.
|
||||
|
||||
```javascript
|
||||
var format = {
|
||||
decimalPlaces: 2,
|
||||
scalingFactor: 1000, // Display in thousands
|
||||
showSign: true,
|
||||
groupingSeparator: ","
|
||||
};
|
||||
```
|
||||
|
||||
### DateFormat
|
||||
|
||||
Date formatting utilities.
|
||||
|
||||
```javascript
|
||||
// Format date
|
||||
var formatted = DateFormat.format(dateValue, "yyyy-MM-dd");
|
||||
// Returns: "2024-01-15"
|
||||
|
||||
// Parse date
|
||||
var parsed = DateFormat.parse("2024-01-15", "yyyy-MM-dd");
|
||||
```
|
||||
|
||||
### StringUtils
|
||||
|
||||
String manipulation.
|
||||
|
||||
```javascript
|
||||
// Available utilities for string operations
|
||||
var trimmed = myString.trim();
|
||||
var upper = myString.toUpperCase();
|
||||
var lower = myString.toLowerCase();
|
||||
```
|
||||
|
||||
### ArrayUtils
|
||||
|
||||
Array operations.
|
||||
|
||||
```javascript
|
||||
// Standard JavaScript array methods
|
||||
var arr = [1, 2, 3, 4, 5];
|
||||
|
||||
// Filter
|
||||
var filtered = arr.filter(function(item) {
|
||||
return item > 2;
|
||||
});
|
||||
|
||||
// Map
|
||||
var doubled = arr.map(function(item) {
|
||||
return item * 2;
|
||||
});
|
||||
|
||||
// Find
|
||||
var found = arr.find(function(item) {
|
||||
return item === 3;
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Enumerations
|
||||
|
||||
### ApplicationMessageType
|
||||
|
||||
```javascript
|
||||
ApplicationMessageType.Info // Informational
|
||||
ApplicationMessageType.Success // Success/confirmation
|
||||
ApplicationMessageType.Warning // Warning
|
||||
ApplicationMessageType.Error // Error
|
||||
```
|
||||
|
||||
### ApplicationMode
|
||||
|
||||
```javascript
|
||||
ApplicationMode.View // Normal view mode
|
||||
ApplicationMode.Present // Presentation mode
|
||||
ApplicationMode.Embed // Embedded in another app
|
||||
```
|
||||
|
||||
### ExportType
|
||||
|
||||
```javascript
|
||||
ExportType.PDF // PDF export
|
||||
ExportType.Excel // Excel export
|
||||
ExportType.CSV // CSV export
|
||||
ExportType.PowerPoint // PowerPoint export
|
||||
```
|
||||
|
||||
### ExportScope
|
||||
|
||||
```javascript
|
||||
ExportScope.All // All content
|
||||
ExportScope.PointOfView // Current point of view only
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging Helpers
|
||||
|
||||
### console.log()
|
||||
|
||||
Print debug information.
|
||||
|
||||
```javascript
|
||||
console.log("Debug message");
|
||||
console.log("Variable:", myVariable);
|
||||
console.log("Object:", JSON.stringify(myObject));
|
||||
```
|
||||
|
||||
**Access Console**:
|
||||
1. Run application
|
||||
2. Press F12 (Developer Tools)
|
||||
3. Go to Console tab
|
||||
4. Look in "sandbox.worker.main.*.js"
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable enhanced debugging:
|
||||
|
||||
```javascript
|
||||
// Add to URL
|
||||
// ?debug=true
|
||||
|
||||
// Or at end of existing URL
|
||||
// ;debug=true
|
||||
```
|
||||
|
||||
### debugger Statement
|
||||
|
||||
Pause execution for step-through debugging.
|
||||
|
||||
```javascript
|
||||
debugger; // Execution pauses here
|
||||
var value = someCalculation();
|
||||
```
|
||||
|
||||
### Performance Logging
|
||||
|
||||
```javascript
|
||||
// Add URL parameter
|
||||
// ?APP_PERFORMANCE_LOGGING=true
|
||||
|
||||
// In console
|
||||
window.sap.raptr.getEntriesByMarker("(Application)")
|
||||
.filter(e => e.entryType === 'measure')
|
||||
.sort((a,b) => (a.startTime + a.duration) - (b.startTime + b.duration));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Examples
|
||||
|
||||
### Example 1: User-Aware Application
|
||||
|
||||
```javascript
|
||||
Application.onInitialization = function() {
|
||||
// Get user info
|
||||
var user = Application.getUserInfo();
|
||||
Text_Welcome.setText("Welcome, " + user.displayName);
|
||||
|
||||
// Check roles
|
||||
var roles = Application.getRolesInfo();
|
||||
var isAdmin = roles.some(function(role) {
|
||||
return role.id === "ADMIN";
|
||||
});
|
||||
|
||||
// Show admin controls if authorized
|
||||
Panel_AdminControls.setVisible(isAdmin);
|
||||
};
|
||||
```
|
||||
|
||||
### Example 2: Responsive Layout
|
||||
|
||||
```javascript
|
||||
Application.onResize = function(width, height) {
|
||||
// Mobile layout (< 768px)
|
||||
if (width < 768) {
|
||||
Chart_1.setVisible(false);
|
||||
Table_1.setVisible(true);
|
||||
Button_ToggleView.setVisible(true);
|
||||
}
|
||||
// Tablet layout (768-1024px)
|
||||
else if (width < 1024) {
|
||||
Chart_1.setVisible(true);
|
||||
Table_1.setVisible(false);
|
||||
Button_ToggleView.setVisible(true);
|
||||
}
|
||||
// Desktop layout (>= 1024px)
|
||||
else {
|
||||
Chart_1.setVisible(true);
|
||||
Table_1.setVisible(true);
|
||||
Button_ToggleView.setVisible(false);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Example 3: Export Report
|
||||
|
||||
```javascript
|
||||
Button_ExportPDF.onClick = function() {
|
||||
var year = Dropdown_Year.getSelectedKey();
|
||||
var region = Dropdown_Region.getSelectedKey();
|
||||
|
||||
Application.export(ExportType.PDF, {
|
||||
scope: ExportScope.All,
|
||||
header: "Sales Report - " + year + " - " + region,
|
||||
footer: "Generated on " + new Date().toLocaleDateString() + " | Page {page}",
|
||||
orientation: "landscape",
|
||||
paperSize: "A4"
|
||||
});
|
||||
};
|
||||
|
||||
Button_ExportExcel.onClick = function() {
|
||||
Table_1.export(ExportType.Excel, {
|
||||
fileName: "SalesData_" + new Date().toISOString().split('T')[0]
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
### Example 4: Navigation with Parameters
|
||||
|
||||
```javascript
|
||||
// Navigate to detail application
|
||||
Button_ViewDetails.onClick = function() {
|
||||
var selections = Table_1.getSelections();
|
||||
if (selections.length > 0) {
|
||||
var productId = selections[0]["Product"];
|
||||
var year = Dropdown_Year.getSelectedKey();
|
||||
|
||||
NavigationUtils.openApplication("PRODUCT_DETAIL_APP", {
|
||||
mode: ApplicationMode.View,
|
||||
newWindow: true,
|
||||
parameters: {
|
||||
p_product: productId,
|
||||
p_year: year
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Warning,
|
||||
"Please select a product first"
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [DataSource API](api-datasource.md)
|
||||
- [Widgets API](api-widgets.md)
|
||||
- [Planning API](api-planning.md)
|
||||
|
||||
**Official Reference**: [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
603
references/api-calendar-bookmarks.md
Normal file
603
references/api-calendar-bookmarks.md
Normal file
@@ -0,0 +1,603 @@
|
||||
# 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](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Calendar Integration API](#calendar-integration-api)
|
||||
2. [Bookmarks API](#bookmarks-api)
|
||||
3. [Linked Analysis API](#linked-analysis-api)
|
||||
4. [Timer API](#timer-api)
|
||||
5. [Notification Badges](#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.
|
||||
|
||||
```javascript
|
||||
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.
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
CalendarTaskStatus.Open // Not started
|
||||
CalendarTaskStatus.InProgress // In progress
|
||||
CalendarTaskStatus.Successful // Completed successfully
|
||||
CalendarTaskStatus.Failed // Failed
|
||||
CalendarTaskStatus.Canceled // Canceled
|
||||
```
|
||||
|
||||
### Reminder Configuration
|
||||
|
||||
```javascript
|
||||
// Add reminder to event
|
||||
event.addReminder({
|
||||
daysBeforeDue: 5,
|
||||
notificationType: "Email"
|
||||
});
|
||||
```
|
||||
|
||||
### Bulk Operations Example
|
||||
|
||||
```javascript
|
||||
// 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.
|
||||
|
||||
```javascript
|
||||
BookmarkSet_1.saveBookmark({
|
||||
name: "Q4 Analysis View",
|
||||
isGlobal: true // false for personal bookmark
|
||||
});
|
||||
```
|
||||
|
||||
#### getAll()
|
||||
|
||||
Returns all available bookmarks.
|
||||
|
||||
```javascript
|
||||
var bookmarks = BookmarkSet_1.getAll();
|
||||
bookmarks.forEach(function(bookmark) {
|
||||
console.log(bookmark.id + ": " + bookmark.name);
|
||||
});
|
||||
```
|
||||
|
||||
#### apply(bookmarkId)
|
||||
|
||||
Applies a saved bookmark.
|
||||
|
||||
```javascript
|
||||
// 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.
|
||||
|
||||
```javascript
|
||||
var currentBookmark = BookmarkSet_1.getAppliedBookmark();
|
||||
if (currentBookmark) {
|
||||
console.log("Current bookmark:", currentBookmark.name);
|
||||
}
|
||||
```
|
||||
|
||||
#### deleteBookmark(bookmarkInfo)
|
||||
|
||||
Deletes a bookmark.
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
// 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.
|
||||
|
||||
```javascript
|
||||
// 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.
|
||||
|
||||
```javascript
|
||||
LinkedAnalysis.removeFilters();
|
||||
```
|
||||
|
||||
### Custom Linked Analysis Pattern
|
||||
|
||||
```javascript
|
||||
// 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:
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```javascript
|
||||
// onTimeout - fires at each interval
|
||||
Timer_1.onTimeout = function() {
|
||||
// Update data or animations
|
||||
refreshDashboard();
|
||||
};
|
||||
```
|
||||
|
||||
### Auto-Refresh Pattern
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```javascript
|
||||
// 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:
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
- [DataSource API](api-datasource.md)
|
||||
- [Widgets API](api-widgets.md)
|
||||
- [Planning API](api-planning.md)
|
||||
- [Application API](api-application.md)
|
||||
|
||||
**Official Reference**: [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
526
references/api-data-operations.md
Normal file
526
references/api-data-operations.md
Normal file
@@ -0,0 +1,526 @@
|
||||
# SAC Data Operations API Reference
|
||||
|
||||
Comprehensive guide for working with data sources, filters, hierarchies, and members in SAP Analytics Cloud scripting.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Data Sources Overview](#data-sources-overview)
|
||||
2. [Range and Exclude Filters](#range-and-exclude-filters)
|
||||
3. [Getting Dimension Filters](#getting-dimension-filters)
|
||||
4. [Dimension Properties](#dimension-properties)
|
||||
5. [Hierarchies](#hierarchies)
|
||||
6. [Getting Members](#getting-members)
|
||||
7. [DataSource Information](#datasource-information)
|
||||
8. [Pattern-Based Functions](#pattern-based-functions)
|
||||
|
||||
---
|
||||
|
||||
## Data Sources Overview
|
||||
|
||||
Data sources are accessed through data-bound widgets:
|
||||
|
||||
```javascript
|
||||
var ds = Chart_1.getDataSource();
|
||||
var ds = Table_1.getDataSource();
|
||||
```
|
||||
|
||||
### Key Method Categories
|
||||
|
||||
| Category | Methods |
|
||||
|----------|---------|
|
||||
| Filters | `setDimensionFilter()`, `removeDimensionFilter()`, `getDimensionFilters()` |
|
||||
| Members | `getMembers()`, `getMember()` |
|
||||
| Dimensions | `getDimensions()`, `getDimensionProperties()` |
|
||||
| Hierarchies | `setHierarchy()`, `setHierarchyLevel()`, `expandNode()`, `collapseNode()` |
|
||||
| Data | `getData()`, `getResultSet()`, `refreshData()` |
|
||||
| Variables | `getVariables()`, `setVariableValue()` |
|
||||
|
||||
---
|
||||
|
||||
## Range and Exclude Filters
|
||||
|
||||
The `DataSource.setDimensionFilter()` method supports both range filters and exclude filters.
|
||||
|
||||
### Exclude Filters
|
||||
|
||||
Filter out specific members from the drill-down.
|
||||
|
||||
**Single Value Include**:
|
||||
```javascript
|
||||
// Keep only employee ID 230 in drill-down
|
||||
DS_1.setDimensionFilter("EMPLOYEE_ID", {value: "230"});
|
||||
```
|
||||
|
||||
**Single Value Exclude**:
|
||||
```javascript
|
||||
// Remove employee ID 230 from drill-down
|
||||
DS_1.setDimensionFilter("EMPLOYEE_ID", {value: "230", exclude: true});
|
||||
```
|
||||
|
||||
**Multiple Values Include**:
|
||||
```javascript
|
||||
// Keep employees 230 and 240 in drill-down
|
||||
DS_1.setDimensionFilter("EMPLOYEE_ID", {values: ["230", "240"]});
|
||||
```
|
||||
|
||||
**Multiple Values Exclude**:
|
||||
```javascript
|
||||
// Remove employees 230 and 240 from drill-down
|
||||
DS_1.setDimensionFilter("EMPLOYEE_ID", {values: ["230", "240"], exclude: true});
|
||||
```
|
||||
|
||||
### Range Filters
|
||||
|
||||
Filter ranges of members in the drill-down.
|
||||
|
||||
**Important Limitations**:
|
||||
- Range filters can **only be applied to numeric dimensions**
|
||||
- A time dimension is **not** a numeric dimension
|
||||
- SAP BW does **not** support numeric dimensions
|
||||
|
||||
**Between Range**:
|
||||
```javascript
|
||||
// Keep employees with IDs between 230 and 240
|
||||
DS_1.setDimensionFilter("EMPLOYEE_ID", {from: "230", to: "240"});
|
||||
```
|
||||
|
||||
**Less Than**:
|
||||
```javascript
|
||||
// Keep employees with IDs less than 230
|
||||
DS_1.setDimensionFilter("EMPLOYEE_ID", {less: "230"});
|
||||
```
|
||||
|
||||
**Less Than or Equal**:
|
||||
```javascript
|
||||
// Keep employees with IDs less than or equal to 230
|
||||
DS_1.setDimensionFilter("EMPLOYEE_ID", {lessOrEqual: "230"});
|
||||
```
|
||||
|
||||
**Greater Than or Equal**:
|
||||
```javascript
|
||||
// Keep employees with IDs greater than or equal to 230
|
||||
DS_1.setDimensionFilter("EMPLOYEE_ID", {greaterOrEqual: "230"});
|
||||
```
|
||||
|
||||
**Greater Than**:
|
||||
```javascript
|
||||
// Keep employees with IDs greater than 230
|
||||
DS_1.setDimensionFilter("EMPLOYEE_ID", {greater: "230"});
|
||||
```
|
||||
|
||||
**Multiple Range Filters**:
|
||||
```javascript
|
||||
// Keep employees with IDs less than 230 OR greater than 240
|
||||
DS_1.setDimensionFilter("EMPLOYEE_ID", [{less: "230"}, {greater: "240"}]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Getting Dimension Filters
|
||||
|
||||
Retrieve current filter values with `getDimensionFilters()`.
|
||||
|
||||
### Method Signature
|
||||
|
||||
```javascript
|
||||
getDimensionFilters(dimension: string | DimensionInfo): FilterValue[]
|
||||
```
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```javascript
|
||||
var values = Table_1.getDataSource().getDimensionFilters("COUNTRY");
|
||||
```
|
||||
|
||||
### Working with Filter Types
|
||||
|
||||
Filter values can be `SingleFilterValue`, `MultipleFilterValue`, or `RangeFilterValue`. Use the `type` property to determine which:
|
||||
|
||||
```javascript
|
||||
var value = Table_1.getDataSource().getDimensionFilters("COUNTRY")[0];
|
||||
|
||||
switch (value.type) {
|
||||
case FilterValueType.Single:
|
||||
var singleValue = cast(Type.SingleFilterValue, value);
|
||||
console.log(singleValue.value);
|
||||
break;
|
||||
|
||||
case FilterValueType.Multiple:
|
||||
var multipleValue = cast(Type.MultipleFilterValue, value);
|
||||
console.log(multipleValue.values); // Array of values
|
||||
break;
|
||||
|
||||
case FilterValueType.Range:
|
||||
var rangeValue = cast(Type.RangeFilterValue, value);
|
||||
console.log(rangeValue.from);
|
||||
console.log(rangeValue.to);
|
||||
// Also available: less, lessOrEqual, greater, greaterOrEqual
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
### Limitations
|
||||
|
||||
- Time range filters are **not** currently returned
|
||||
- SAP BW may have valid filters not supported by SAP Analytics Cloud
|
||||
|
||||
---
|
||||
|
||||
## Dimension Properties
|
||||
|
||||
Retrieve dimension properties of a data source.
|
||||
|
||||
### Method Signature
|
||||
|
||||
```javascript
|
||||
getDimensionProperties(dimension: string | DimensionInfo): DimensionPropertyInfo[]
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```javascript
|
||||
var properties = Table_1.getDataSource().getDimensionProperties("Location");
|
||||
|
||||
for (var i = 0; i < properties.length; i++) {
|
||||
console.log("Property: " + properties[i].id);
|
||||
console.log("Description: " + properties[i].description);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hierarchies
|
||||
|
||||
Set hierarchy levels and expand/collapse nodes.
|
||||
|
||||
**Note**: Currently supported only by data sources of **Table** and **Chart** widgets.
|
||||
|
||||
### Set Hierarchy Level
|
||||
|
||||
```javascript
|
||||
DataSource.setHierarchyLevel(dimension: string|DimensionInfo, level?: integer): void
|
||||
DataSource.getHierarchyLevel(dimension: string|DimensionInfo): integer
|
||||
```
|
||||
|
||||
**Chart Example**:
|
||||
```javascript
|
||||
var ds = Chart_1.getDataSource();
|
||||
ds.setHierarchy("Location_4nm2e04531", "State_47acc246_4m5x6u3k6s");
|
||||
ds.setHierarchyLevel("Location_4nm2e04531", 2);
|
||||
```
|
||||
|
||||
**Table Example**:
|
||||
```javascript
|
||||
var ds = Table_1.getDataSource();
|
||||
ds.setHierarchy("Location_4nm2e04531", "State_47acc246_4m5x6u3k6s");
|
||||
ds.setHierarchyLevel("Location_4nm2e04531", 2);
|
||||
```
|
||||
|
||||
### Expand/Collapse Hierarchy Nodes
|
||||
|
||||
```javascript
|
||||
DataSource.expandNode(dimension: string|DimensionInfo, selection: Selection): void
|
||||
DataSource.collapseNode(dimension: string|DimensionInfo, selection: Selection): void
|
||||
```
|
||||
|
||||
**Expand Single Node (Chart)**:
|
||||
```javascript
|
||||
// Chart with Location in category axis, hierarchy level 1
|
||||
// Expand California node
|
||||
Chart_1.getDataSource().expandNode("Location_4nm2e04531", {
|
||||
"Location_4nm2e04531": "[Location_4nm2e04531].[State_47acc246_4m5x6u3k6s].&[SA1]",
|
||||
"@MeasureDimension": "[Account_BestRunJ_sold].[parentId].&[Discount]"
|
||||
});
|
||||
```
|
||||
|
||||
**Expand All Nodes with Specific Value (Chart)**:
|
||||
```javascript
|
||||
// Chart with Location and Product in category axis
|
||||
// Expand all Alcohol nodes
|
||||
Chart_1.getDataSource().expandNode("Product_3e315003an", {
|
||||
"Product_3e315003an": "[Product_3e315003an].[Product_Catego_3o3x5e06y2].&[PC4]",
|
||||
"@MeasureDimension": "[Account_BestRunJ_sold].[parentId].&[Discount]"
|
||||
});
|
||||
```
|
||||
|
||||
**Expand Specific Node (Table)**:
|
||||
```javascript
|
||||
// Table with Location and Product in rows
|
||||
// Expand California + Alcohol node
|
||||
Table_1.getDataSource().expandNode("Location_4nm2e04531", {
|
||||
"Product_3e315003an": "[Product_3e315003an].[Product_Catego_3o3x5e06y2].&[PC4]",
|
||||
"Location_4nm2e04531": "[Location_4nm2e04531].[State_47acc246_4m5x6u3k6s].&[SA1]",
|
||||
"@MeasureDimension": "[Account_BestRunJ_sold].[parentId].&[Discount]"
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Getting Members
|
||||
|
||||
### Get Single Member
|
||||
|
||||
```javascript
|
||||
DataSource.getMember(
|
||||
dimension: string | DimensionInfo,
|
||||
memberId: string,
|
||||
hierarchy?: string | HierarchyInfo
|
||||
): MemberInfo
|
||||
```
|
||||
|
||||
**Flat Hierarchy**:
|
||||
```javascript
|
||||
// With flat presentation hierarchy
|
||||
Table_1.getDataSource().getMember("Location_4nm2e04531", "CT1");
|
||||
// Returns: {id: 'CT1', description: 'Los Angeles', dimensionId: 'Location_4nm2e04531', displayId: 'CT1'}
|
||||
```
|
||||
|
||||
**With Specific Hierarchy**:
|
||||
```javascript
|
||||
// With State hierarchy active
|
||||
Table_1.getDataSource().getMember(
|
||||
"Location_4nm2e04531",
|
||||
"[Location_4nm2e04531].[State_47acc246_4m5x6u3k6s].&[CT1]"
|
||||
);
|
||||
// Returns: {id: '[Location_4nm2e04531].[State_47acc246_4m5x6u3k6s].&[CT1]', description: 'Los Angeles', ...}
|
||||
```
|
||||
|
||||
**Important**: Member ID format depends on the active hierarchy:
|
||||
- Flat hierarchy: `"CT1"`
|
||||
- Actual hierarchy: `"[Location_4nm2e04531].[State_47acc246_4m5x6u3k6s].&[CT1]"`
|
||||
|
||||
### Get Multiple Members
|
||||
|
||||
```javascript
|
||||
DataSource.getMembers(
|
||||
dimension: string | DimensionInfo,
|
||||
options?: integer | MembersOptions
|
||||
): MemberInfo[]
|
||||
```
|
||||
|
||||
**Basic Usage (Limit Count)**:
|
||||
```javascript
|
||||
// Get first 3 members
|
||||
Table_1.getDataSource().getMembers("Location_4nm2e04531", 3);
|
||||
// Returns array of 3 MemberInfo objects
|
||||
```
|
||||
|
||||
**With Options Object**:
|
||||
```javascript
|
||||
Table_1.getDataSource().getMembers("Location_4nm2e04531", {limit: 3});
|
||||
```
|
||||
|
||||
### MembersOptions
|
||||
|
||||
```javascript
|
||||
{
|
||||
// Type of members: MemberAccessMode.MasterData (default) or MemberAccessMode.BookedValues
|
||||
accessMode: MemberAccessMode,
|
||||
|
||||
// Hierarchy ID (default: currently active hierarchy)
|
||||
hierarchyId: string,
|
||||
|
||||
// Maximum number of returned members (default: 200)
|
||||
limit: integer
|
||||
}
|
||||
```
|
||||
|
||||
**With Hierarchy**:
|
||||
```javascript
|
||||
Table_1.getDataSource().getMembers("Location_4nm2e04531", {
|
||||
limit: 2,
|
||||
hierarchyId: "State_47acc246_4m5x6u3k6s"
|
||||
});
|
||||
```
|
||||
|
||||
**Master Data vs Booked Values**:
|
||||
|
||||
```javascript
|
||||
// Master Data (all possible members)
|
||||
Table_1.getDataSource().getMembers("Location_4nm2e04531", {
|
||||
accessMode: MemberAccessMode.MasterData
|
||||
});
|
||||
// Returns all members including states
|
||||
|
||||
// Booked Values (only members with data)
|
||||
Table_1.getDataSource().getMembers("Location_4nm2e04531", {
|
||||
accessMode: MemberAccessMode.BookedValues
|
||||
});
|
||||
// Returns only members that have actual data
|
||||
```
|
||||
|
||||
**Tip**: To find booked values:
|
||||
1. Create table with dimension
|
||||
2. Set desired hierarchy
|
||||
3. Open ... menu → Deselect "Unbooked Values"
|
||||
4. Table shows only booked values
|
||||
|
||||
---
|
||||
|
||||
## DataSource Information
|
||||
|
||||
Get metadata about a data source.
|
||||
|
||||
### Method Signature
|
||||
|
||||
```javascript
|
||||
DataSource.getInfo(): DataSourceInfo
|
||||
```
|
||||
|
||||
### DataSourceInfo Properties
|
||||
|
||||
```javascript
|
||||
class DataSourceInfo {
|
||||
modelName: string,
|
||||
modelId: string,
|
||||
modelDescription: string,
|
||||
sourceName: string, // SAP BW only
|
||||
sourceDescription: string, // SAP BW only
|
||||
sourceLastChangedBy: string, // SAP BW only
|
||||
sourceLastRefreshedAt: Date // SAP BW only
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: `sourceName`, `sourceDescription`, `sourceLastChangedBy`, and `sourceLastRefreshedAt` are only supported for SAP BW models. For other models they return `undefined`.
|
||||
|
||||
### Usage Example
|
||||
|
||||
```javascript
|
||||
var dsInfo = Table_1.getDataSource().getInfo();
|
||||
|
||||
console.log("Model name: " + dsInfo.modelName);
|
||||
console.log("Model ID: " + dsInfo.modelId);
|
||||
console.log("Model description: " + dsInfo.modelDescription);
|
||||
console.log("Source name: " + dsInfo.sourceName);
|
||||
console.log("Source description: " + dsInfo.sourceDescription);
|
||||
console.log("Source last changed by: " + dsInfo.sourceLastChangedBy);
|
||||
|
||||
var strLastRefresh = "undefined";
|
||||
if (dsInfo.sourceLastRefreshedAt !== undefined) {
|
||||
strLastRefresh = dsInfo.sourceLastRefreshedAt.toISOString();
|
||||
}
|
||||
console.log("Source last refreshed at: " + strLastRefresh);
|
||||
```
|
||||
|
||||
**SAP BW Output**:
|
||||
```
|
||||
Model name: HAL_TEST_Scenario_Query
|
||||
Model ID: t.H:C9gjfpmu5ntxaf3dbfwtyl5wab
|
||||
Model description: Sample scenario query
|
||||
Source name: TEST_SCENARIO_QUERY
|
||||
Source description: Test Query Scenario
|
||||
Source last changed by: SYSTEM
|
||||
Source last refreshed at: 2021-09-23T22:00:00.000Z
|
||||
```
|
||||
|
||||
**SAP HANA Output**:
|
||||
```
|
||||
Model name: BestRunJuice_SampleModel
|
||||
Model ID: t.2.CMRCZ9NPY3VAER9AO6PT80G12:...
|
||||
Model description: Sample Model
|
||||
Source name: undefined
|
||||
Source description: undefined
|
||||
Source last changed by: undefined
|
||||
Source last refreshed at: undefined
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern-Based Functions
|
||||
|
||||
Create string transformation functions using input/output examples instead of code.
|
||||
|
||||
### Adding Pattern-Based Function
|
||||
|
||||
1. In Outline, add a **ScriptObject**
|
||||
2. Choose **...** → **Add Pattern Based Function**
|
||||
3. Define function name and description
|
||||
|
||||
### Creating the Pattern
|
||||
|
||||
1. Click **+** next to "Create Pattern"
|
||||
2. Define **Training Example**: Input → Output mapping
|
||||
- Example: `john.doe@sap.com` → `John Doe`
|
||||
3. Click **Create** to generate pattern via machine learning
|
||||
4. Add more examples if ambiguous (up to 3)
|
||||
5. Click **+** next to "Verify Pattern" to test
|
||||
6. Click **Done** when complete
|
||||
|
||||
### Using in Scripts
|
||||
|
||||
```javascript
|
||||
var fullName = ScriptObject_1.myPatternBasedFunction("joe.doe@sap.com");
|
||||
// Returns: "Joe Doe"
|
||||
```
|
||||
|
||||
### Example: Date Transformation
|
||||
|
||||
Transform dates from `MM.DD.YYYY` to `DD.MM.YY`:
|
||||
|
||||
**Training Examples**:
|
||||
- Input: `10.11.2011` → Output: `11.10.11`
|
||||
- Input: `09.05.2020` → Output: `05.09.20` (needed for disambiguation)
|
||||
|
||||
### Example: String Extraction
|
||||
|
||||
Transform appointment text to structured format:
|
||||
|
||||
**Input**: `John Doe has an appointment on 06.07.20 at 3:00pm.`
|
||||
**Output**: `Name: John Doe, Date: 06.07.20, Time: 3:00pm`
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
- If pattern defaults to returning input, add more training examples
|
||||
- Click **Reset** to undo changes and restore last working pattern
|
||||
- Maximum 3 training examples supported
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Filter Performance
|
||||
|
||||
```javascript
|
||||
// GOOD: Pause refresh before multiple filters
|
||||
var ds = Table_1.getDataSource();
|
||||
ds.setRefreshPaused(true);
|
||||
|
||||
ds.setDimensionFilter("Year", "2024");
|
||||
ds.setDimensionFilter("Region", "EMEA");
|
||||
ds.setDimensionFilter("Product", "Widget");
|
||||
|
||||
ds.setRefreshPaused(false); // Single backend call
|
||||
|
||||
// BAD: Each filter triggers refresh
|
||||
ds.setDimensionFilter("Year", "2024"); // Refresh
|
||||
ds.setDimensionFilter("Region", "EMEA"); // Refresh
|
||||
ds.setDimensionFilter("Product", "Widget"); // Refresh
|
||||
```
|
||||
|
||||
### Member Retrieval
|
||||
|
||||
```javascript
|
||||
// GOOD: Use getResultSet() when possible (no backend trip)
|
||||
var resultSet = Chart_1.getDataSource().getResultSet();
|
||||
|
||||
// EXPENSIVE: getMembers() always hits backend
|
||||
var members = Chart_1.getDataSource().getMembers("Dimension");
|
||||
|
||||
// GOOD: Specify limit to reduce data transfer
|
||||
var members = ds.getMembers("Dimension", {limit: 50});
|
||||
|
||||
// GOOD: Use BookedValues when you only need data with values
|
||||
var members = ds.getMembers("Dimension", {
|
||||
accessMode: MemberAccessMode.BookedValues
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Source**: SAP Analytics Designer Development Guide - Chapter 4: Scripting in Analytics Designer
|
||||
**Last Updated**: 2025-11-23
|
||||
463
references/api-datasource.md
Normal file
463
references/api-datasource.md
Normal file
@@ -0,0 +1,463 @@
|
||||
# DataSource API Reference
|
||||
|
||||
Complete reference for the DataSource API in SAP Analytics Cloud scripting.
|
||||
|
||||
**Source**: [Analytics Designer API Reference 2025.14](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Getting DataSource Reference](#getting-datasource-reference)
|
||||
2. [Information Methods](#information-methods)
|
||||
3. [Dimension Methods](#dimension-methods)
|
||||
4. [Measure Methods](#measure-methods)
|
||||
5. [Filter Methods](#filter-methods)
|
||||
6. [Variable Methods](#variable-methods)
|
||||
7. [Data Access Methods](#data-access-methods)
|
||||
8. [Hierarchy Methods](#hierarchy-methods)
|
||||
9. [Refresh Control](#refresh-control)
|
||||
10. [DataSource Types](#datasource-types)
|
||||
|
||||
---
|
||||
|
||||
## Getting DataSource Reference
|
||||
|
||||
DataSources cannot be referenced directly. Obtain reference via widget:
|
||||
|
||||
```javascript
|
||||
// From Chart
|
||||
var ds = Chart_1.getDataSource();
|
||||
|
||||
// From Table
|
||||
var ds = Table_1.getDataSource();
|
||||
|
||||
// From GeoMap Layer
|
||||
var ds = GeoMap_1.getLayer("LayerName").getDataSource();
|
||||
```
|
||||
|
||||
Returns `undefined` if widget has no data binding.
|
||||
|
||||
---
|
||||
|
||||
## Information Methods
|
||||
|
||||
### getInfo()
|
||||
|
||||
Returns information about the data source.
|
||||
|
||||
```javascript
|
||||
var info = ds.getInfo();
|
||||
// Returns: DataSourceInfo object
|
||||
// - id: string
|
||||
// - description: string
|
||||
// - modelId: string
|
||||
// - type: DataSourceType
|
||||
```
|
||||
|
||||
### getDataSourceInfo()
|
||||
|
||||
Returns detailed data source metadata.
|
||||
|
||||
```javascript
|
||||
var dsInfo = ds.getDataSourceInfo();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dimension Methods
|
||||
|
||||
### getDimensions()
|
||||
|
||||
Returns all dimensions of the data source.
|
||||
|
||||
```javascript
|
||||
var dimensions = ds.getDimensions();
|
||||
// Returns: Array of DimensionInfo objects
|
||||
// Each contains: { id: string, description: string }
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
var dims = Chart_1.getDataSource().getDimensions();
|
||||
for (var i = 0; i < dims.length; i++) {
|
||||
console.log(dims[i].id + ": " + dims[i].description);
|
||||
}
|
||||
```
|
||||
|
||||
### getMembers(dimensionId, options)
|
||||
|
||||
Returns members of a dimension.
|
||||
|
||||
**Parameters**:
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| dimensionId | string | Yes | Dimension identifier |
|
||||
| options | object | No | Filter options |
|
||||
|
||||
**Options**:
|
||||
- `accessMode`: `MemberAccessMode.BookedValues` | `MemberAccessMode.All`
|
||||
- `hierarchyId`: string - Specific hierarchy
|
||||
- `level`: number - Hierarchy level
|
||||
|
||||
```javascript
|
||||
// All members
|
||||
var members = ds.getMembers("Location");
|
||||
|
||||
// Booked values only
|
||||
var bookedMembers = ds.getMembers("Location", {
|
||||
accessMode: MemberAccessMode.BookedValues
|
||||
});
|
||||
|
||||
// Specific hierarchy level
|
||||
var levelMembers = ds.getMembers("Date", {
|
||||
hierarchyId: "YQM",
|
||||
level: 2
|
||||
});
|
||||
```
|
||||
|
||||
**Returns**: Array of MemberInfo objects
|
||||
```javascript
|
||||
{
|
||||
dimensionId: string,
|
||||
id: string, // Full qualified ID
|
||||
description: string,
|
||||
displayId: string, // Short ID
|
||||
properties: object // Custom attributes
|
||||
}
|
||||
```
|
||||
|
||||
**Performance Note**: `getMembers()` always triggers backend request. Use `getResultSet()` when possible.
|
||||
|
||||
### getMember(dimensionId, memberId)
|
||||
|
||||
Returns specific member info.
|
||||
|
||||
```javascript
|
||||
var member = ds.getMember("Location", "[Location].[Country].&[US]");
|
||||
```
|
||||
|
||||
### getMemberDisplayMode(dimensionId)
|
||||
|
||||
Returns display mode for dimension (Key, Description, Text, etc.).
|
||||
|
||||
```javascript
|
||||
var mode = ds.getMemberDisplayMode("Product");
|
||||
// Returns: "Description" | "Key" | "KeyDescription" | etc.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Measure Methods
|
||||
|
||||
### getMeasures()
|
||||
|
||||
Returns all measures of the data source.
|
||||
|
||||
```javascript
|
||||
var measures = ds.getMeasures();
|
||||
// Returns: Array of MeasureInfo objects
|
||||
// dimensionId is always "@MeasureDimension"
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
var measures = Chart_1.getDataSource().getMeasures();
|
||||
measures.forEach(function(m) {
|
||||
console.log(m.id + ": " + m.description);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Filter Methods
|
||||
|
||||
### setDimensionFilter(dimensionId, filterValue)
|
||||
|
||||
Sets filter on a dimension. Overwrites existing filter.
|
||||
|
||||
```javascript
|
||||
// Single value
|
||||
ds.setDimensionFilter("Year", "2024");
|
||||
|
||||
// Qualified member ID
|
||||
ds.setDimensionFilter("Date", "[Date].[YQM].&[2024]");
|
||||
|
||||
// Multiple values (array)
|
||||
ds.setDimensionFilter("Region", ["EMEA", "APAC", "AMER"]);
|
||||
```
|
||||
|
||||
**Important**: Does not affect Advanced Filters set in designer.
|
||||
|
||||
### removeDimensionFilter(dimensionId)
|
||||
|
||||
Removes filter from dimension.
|
||||
|
||||
```javascript
|
||||
ds.removeDimensionFilter("Year");
|
||||
```
|
||||
|
||||
### getDimensionFilters(dimensionId)
|
||||
|
||||
Returns current filters on a dimension.
|
||||
|
||||
```javascript
|
||||
var filters = ds.getDimensionFilters("Year");
|
||||
// Returns: Array of FilterInfo objects
|
||||
// { value: string, type: "Single" | "Range" | "Multiple" }
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
var yearFilter = ds.getDimensionFilters("Year")[0];
|
||||
console.log(yearFilter);
|
||||
// { value: '[Date].[YQM].&[2024]', type: 'Single' }
|
||||
```
|
||||
|
||||
### copyDimensionFilterFrom(sourceDataSource, dimensionId?)
|
||||
|
||||
Copies filters from another data source.
|
||||
|
||||
```javascript
|
||||
// Copy all filters
|
||||
Table_1.getDataSource().copyDimensionFilterFrom(Chart_1.getDataSource());
|
||||
|
||||
// Copy specific dimension filter
|
||||
Table_1.getDataSource().copyDimensionFilterFrom(
|
||||
Chart_1.getDataSource(),
|
||||
"Location"
|
||||
);
|
||||
```
|
||||
|
||||
**Performance Tip**: More efficient than setting filters individually.
|
||||
|
||||
### clearAllFilters()
|
||||
|
||||
Removes all dimension filters.
|
||||
|
||||
```javascript
|
||||
ds.clearAllFilters();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Variable Methods
|
||||
|
||||
### getVariables()
|
||||
|
||||
Returns all variables of the data source.
|
||||
|
||||
```javascript
|
||||
var variables = ds.getVariables();
|
||||
// Returns: Array of VariableInfo objects
|
||||
```
|
||||
|
||||
### getVariableValues(variableId)
|
||||
|
||||
Returns values of a variable.
|
||||
|
||||
```javascript
|
||||
var values = ds.getVariableValues("VAR_YEAR");
|
||||
// Returns: Array of VariableValue objects
|
||||
```
|
||||
|
||||
### setVariableValue(variableId, value)
|
||||
|
||||
Sets value of a variable.
|
||||
|
||||
```javascript
|
||||
ds.setVariableValue("VAR_YEAR", "2024");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Access Methods
|
||||
|
||||
### getData(selection)
|
||||
|
||||
Returns data values for a specific selection.
|
||||
|
||||
```javascript
|
||||
var selection = {
|
||||
"@MeasureDimension": "[Account].[parentId].&[Revenue]",
|
||||
"Location": "[Location].[Country].&[US]"
|
||||
};
|
||||
|
||||
var data = ds.getData(selection);
|
||||
// Returns: { formattedValue: string, rawValue: number }
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
var data = Chart_1.getDataSource().getData({
|
||||
"@MeasureDimension": "[Account].[parentId].&[Quantity_sold]",
|
||||
"Location": "[Location].[State].&[CA]"
|
||||
});
|
||||
console.log("Formatted:", data.formattedValue);
|
||||
console.log("Raw:", data.rawValue);
|
||||
```
|
||||
|
||||
### getResultSet()
|
||||
|
||||
Returns current result set without backend trip.
|
||||
|
||||
```javascript
|
||||
var resultSet = ds.getResultSet();
|
||||
```
|
||||
|
||||
**Performance**: Significantly faster than `getMembers()`. Use when possible.
|
||||
|
||||
### getResultMember(dimensionId, selection)
|
||||
|
||||
Returns member details from result set.
|
||||
|
||||
```javascript
|
||||
var member = ds.getResultMember("Location", selection);
|
||||
// Includes parent relationship info
|
||||
```
|
||||
|
||||
### getDataSelections()
|
||||
|
||||
Returns current data selections as key-value pairs.
|
||||
|
||||
```javascript
|
||||
var selections = ds.getDataSelections();
|
||||
// Returns: Object mapping dimension IDs to member IDs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hierarchy Methods
|
||||
|
||||
### getHierarchies(dimensionId)
|
||||
|
||||
Returns hierarchies for a dimension.
|
||||
|
||||
```javascript
|
||||
var hierarchies = ds.getHierarchies("Date");
|
||||
// Returns: Array of { id: string, description: string }
|
||||
```
|
||||
|
||||
### collapseNode(dimensionId, selection)
|
||||
|
||||
Collapses hierarchy node.
|
||||
|
||||
```javascript
|
||||
ds.collapseNode("Date", {
|
||||
"@MeasureDimension": "[Account].[Revenue]",
|
||||
"Date": "[Date].[YQM].&[2024]"
|
||||
});
|
||||
```
|
||||
|
||||
### expandNode(dimensionId, selection)
|
||||
|
||||
Expands hierarchy node.
|
||||
|
||||
```javascript
|
||||
ds.expandNode("Date", selection);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Refresh Control
|
||||
|
||||
### refreshData()
|
||||
|
||||
Refreshes data from backend.
|
||||
|
||||
```javascript
|
||||
ds.refreshData();
|
||||
```
|
||||
|
||||
### setRefreshPaused(paused)
|
||||
|
||||
Pauses or resumes automatic refresh.
|
||||
|
||||
```javascript
|
||||
// Pause before multiple operations
|
||||
ds.setRefreshPaused(true);
|
||||
|
||||
// Apply multiple changes
|
||||
ds.setDimensionFilter("Year", "2024");
|
||||
ds.setDimensionFilter("Region", "EMEA");
|
||||
ds.setDimensionFilter("Product", "Widget");
|
||||
|
||||
// Resume (single backend call)
|
||||
ds.setRefreshPaused(false);
|
||||
```
|
||||
|
||||
**Best Practice**: Always use for batch filter operations.
|
||||
|
||||
### isRefreshPaused()
|
||||
|
||||
Returns current pause state.
|
||||
|
||||
```javascript
|
||||
if (ds.isRefreshPaused()) {
|
||||
console.log("Refresh is paused");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DataSource Types
|
||||
|
||||
### DataSourceType Enumeration
|
||||
|
||||
```javascript
|
||||
DataSourceType.Model // SAC model
|
||||
DataSourceType.Query // Live query
|
||||
DataSourceType.Calculation // Calculated source
|
||||
```
|
||||
|
||||
### MemberAccessMode Enumeration
|
||||
|
||||
```javascript
|
||||
MemberAccessMode.All // All master data members
|
||||
MemberAccessMode.BookedValues // Only members with data
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Example
|
||||
|
||||
```javascript
|
||||
// Complex filtering scenario
|
||||
function applyComplexFilters(chartDs, tableDs, year, region, product) {
|
||||
// Pause both data sources
|
||||
chartDs.setRefreshPaused(true);
|
||||
tableDs.setRefreshPaused(true);
|
||||
|
||||
try {
|
||||
// Apply filters to chart
|
||||
chartDs.setDimensionFilter("Year", year);
|
||||
chartDs.setDimensionFilter("Region", region);
|
||||
chartDs.setDimensionFilter("Product", product);
|
||||
|
||||
// Copy filters to table
|
||||
tableDs.copyDimensionFilterFrom(chartDs);
|
||||
|
||||
} catch (error) {
|
||||
console.log("Error applying filters:", error);
|
||||
}
|
||||
|
||||
// Resume both (triggers single refresh each)
|
||||
chartDs.setRefreshPaused(false);
|
||||
tableDs.setRefreshPaused(false);
|
||||
}
|
||||
|
||||
// Usage
|
||||
var chartDs = Chart_1.getDataSource();
|
||||
var tableDs = Table_1.getDataSource();
|
||||
applyComplexFilters(chartDs, tableDs, "2024", "EMEA", "Widget-A");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Widgets API](api-widgets.md)
|
||||
- [Planning API](api-planning.md)
|
||||
- [Application API](api-application.md)
|
||||
|
||||
**Official Reference**: [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
541
references/api-planning.md
Normal file
541
references/api-planning.md
Normal file
@@ -0,0 +1,541 @@
|
||||
# Planning API Reference
|
||||
|
||||
Complete reference for planning operations in SAP Analytics Cloud scripting.
|
||||
|
||||
**Source**: [Analytics Designer API Reference 2025.14](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Getting Planning Reference](#getting-planning-reference)
|
||||
2. [Version Management](#version-management)
|
||||
3. [Data Operations](#data-operations)
|
||||
4. [Data Locking](#data-locking)
|
||||
5. [Data Actions](#data-actions)
|
||||
6. [Multi-Actions](#multi-actions)
|
||||
7. [Planning Enumerations](#planning-enumerations)
|
||||
8. [Complete Examples](#complete-examples)
|
||||
|
||||
---
|
||||
|
||||
## Getting Planning Reference
|
||||
|
||||
Access Planning API via Table widget:
|
||||
|
||||
```javascript
|
||||
var planning = Table_1.getPlanning();
|
||||
```
|
||||
|
||||
**Note**: Planning features require a planning-enabled model connected to the table.
|
||||
|
||||
---
|
||||
|
||||
## Version Management
|
||||
|
||||
### Public Versions
|
||||
|
||||
#### getPublicVersion(versionId)
|
||||
|
||||
Returns a public version object.
|
||||
|
||||
```javascript
|
||||
var budgetVersion = Table_1.getPlanning().getPublicVersion("Budget2024");
|
||||
```
|
||||
|
||||
#### getPublicVersions()
|
||||
|
||||
Returns all public versions.
|
||||
|
||||
```javascript
|
||||
var allPublicVersions = Table_1.getPlanning().getPublicVersions();
|
||||
```
|
||||
|
||||
### Private Versions
|
||||
|
||||
#### getPrivateVersion(versionId)
|
||||
|
||||
Returns a private version object.
|
||||
|
||||
```javascript
|
||||
var draftVersion = Table_1.getPlanning().getPrivateVersion("myDraft");
|
||||
```
|
||||
|
||||
#### getPrivateVersions()
|
||||
|
||||
Returns all private versions.
|
||||
|
||||
```javascript
|
||||
var allPrivateVersions = Table_1.getPlanning().getPrivateVersions();
|
||||
```
|
||||
|
||||
#### createPrivateVersion(versionId, options)
|
||||
|
||||
Creates a new private version.
|
||||
|
||||
```javascript
|
||||
var newDraft = Table_1.getPlanning()
|
||||
.createPrivateVersion("Draft_" + Date.now().toString());
|
||||
```
|
||||
|
||||
### Version Object Methods
|
||||
|
||||
#### isDirty()
|
||||
|
||||
Checks if version has unsaved changes.
|
||||
|
||||
```javascript
|
||||
var version = Table_1.getPlanning().getPublicVersion("Budget2024");
|
||||
if (version.isDirty()) {
|
||||
console.log("Version has unsaved changes");
|
||||
}
|
||||
```
|
||||
|
||||
#### publish()
|
||||
|
||||
Publishes changes to the version.
|
||||
|
||||
```javascript
|
||||
var version = Table_1.getPlanning().getPublicVersion("Budget2024");
|
||||
if (version) {
|
||||
if (version.isDirty()) {
|
||||
version.publish();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### copy(targetVersionId, copyOption, category?)
|
||||
|
||||
Copies version to a new version.
|
||||
|
||||
**Parameters**:
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| targetVersionId | string | Yes | Name for new version |
|
||||
| copyOption | PlanningCopyOption | Yes | What to copy |
|
||||
| category | PlanningCategory | No | Version category |
|
||||
|
||||
```javascript
|
||||
// Copy public version to new private version
|
||||
var budgetVersion = Table_1.getPlanning().getPublicVersion("Budget2024");
|
||||
if (budgetVersion) {
|
||||
budgetVersion.copy(
|
||||
"Budget_Copy_" + Date.now().toString(),
|
||||
PlanningCopyOption.PlanningArea
|
||||
);
|
||||
}
|
||||
|
||||
// Copy with category
|
||||
var sourceVersion = Table_1.getPlanning().getPrivateVersion("Source");
|
||||
if (sourceVersion) {
|
||||
sourceVersion.copy(
|
||||
"TargetVersion",
|
||||
PlanningCopyOption.AllData,
|
||||
PlanningCategory.Budget
|
||||
);
|
||||
}
|
||||
|
||||
// Copy as Forecast
|
||||
var forecastSource = Table_1.getPlanning().getPrivateVersion("ForecastDraft");
|
||||
if (forecastSource) {
|
||||
forecastSource.copy(
|
||||
"Forecast_" + Date.now().toString(),
|
||||
PlanningCopyOption.PlanningArea,
|
||||
PlanningCategory.Forecast
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### revert()
|
||||
|
||||
Reverts unsaved changes in the version.
|
||||
|
||||
```javascript
|
||||
var version = Table_1.getPlanning().getPublicVersion("Budget2024");
|
||||
version.revert();
|
||||
```
|
||||
|
||||
#### delete()
|
||||
|
||||
Deletes a private version.
|
||||
|
||||
```javascript
|
||||
var draft = Table_1.getPlanning().getPrivateVersion("OldDraft");
|
||||
if (draft) {
|
||||
draft.delete();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Operations
|
||||
|
||||
### submitData()
|
||||
|
||||
Submits pending data changes.
|
||||
|
||||
```javascript
|
||||
Table_1.getPlanning().submitData();
|
||||
```
|
||||
|
||||
### getPlanningArea()
|
||||
|
||||
Returns planning area configuration.
|
||||
|
||||
```javascript
|
||||
var planningArea = Table_1.getPlanning().getPlanningArea();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Locking
|
||||
|
||||
### getDataLocking()
|
||||
|
||||
Returns data locking interface.
|
||||
|
||||
```javascript
|
||||
var dataLocking = Table_1.getPlanning().getDataLocking();
|
||||
```
|
||||
|
||||
### getState(selection)
|
||||
|
||||
Returns lock state for a selection.
|
||||
|
||||
```javascript
|
||||
var selection = Table_1.getSelections()[0];
|
||||
var lockState = Table_1.getPlanning().getDataLocking().getState(selection);
|
||||
```
|
||||
|
||||
**Lock States**:
|
||||
- `Locked` - Data is locked by another user
|
||||
- `Unlocked` - Data is available for editing
|
||||
- `LockedByMe` - Data is locked by current user
|
||||
|
||||
### Example: Check Before Edit
|
||||
|
||||
```javascript
|
||||
var selection = Table_1.getSelections()[0];
|
||||
var lockState = Table_1.getPlanning().getDataLocking().getState(selection);
|
||||
|
||||
if (lockState === DataLockingState.Locked) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Warning,
|
||||
"Data is locked by another user"
|
||||
);
|
||||
} else {
|
||||
// Proceed with edit
|
||||
performEdit();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Actions
|
||||
|
||||
Execute predefined data actions.
|
||||
|
||||
### Execute Data Action
|
||||
|
||||
```javascript
|
||||
// Simple execution
|
||||
DataAction_1.execute();
|
||||
|
||||
// With parameters
|
||||
DataAction_1.setParameterValue("YEAR", "2024");
|
||||
DataAction_1.setParameterValue("VERSION", "Budget");
|
||||
DataAction_1.execute();
|
||||
```
|
||||
|
||||
### Data Action Events
|
||||
|
||||
```javascript
|
||||
DataAction_1.onBeforeExecute = function() {
|
||||
Application.showBusyIndicator();
|
||||
};
|
||||
|
||||
DataAction_1.onAfterExecute = function(result) {
|
||||
Application.hideBusyIndicator();
|
||||
if (result.success) {
|
||||
Application.showMessage(ApplicationMessageType.Info, "Data action completed");
|
||||
} else {
|
||||
Application.showMessage(ApplicationMessageType.Error, "Data action failed");
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Background Execution
|
||||
|
||||
```javascript
|
||||
// Execute in background
|
||||
DataAction_1.executeInBackground();
|
||||
|
||||
// Monitor status
|
||||
DataAction_1.onExecutionStatusUpdate = function(status) {
|
||||
console.log("Status:", status);
|
||||
if (status === ExecutionStatus.Completed) {
|
||||
Table_1.getDataSource().refreshData();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Multi-Actions
|
||||
|
||||
Execute multiple data actions in sequence.
|
||||
|
||||
### Execute Multi-Action
|
||||
|
||||
```javascript
|
||||
MultiAction_1.execute();
|
||||
```
|
||||
|
||||
### With Parameters
|
||||
|
||||
```javascript
|
||||
MultiAction_1.setParameterValue("YEAR", "2024");
|
||||
MultiAction_1.setParameterValue("SCENARIO", "Actual");
|
||||
MultiAction_1.execute();
|
||||
```
|
||||
|
||||
### Events
|
||||
|
||||
```javascript
|
||||
MultiAction_1.onBeforeExecute = function() {
|
||||
Application.showBusyIndicator();
|
||||
};
|
||||
|
||||
MultiAction_1.onAfterExecute = function(result) {
|
||||
Application.hideBusyIndicator();
|
||||
Table_1.getDataSource().refreshData();
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Planning Enumerations
|
||||
|
||||
### PlanningCopyOption
|
||||
|
||||
```javascript
|
||||
PlanningCopyOption.AllData // Copy all data
|
||||
PlanningCopyOption.PlanningArea // Copy planning area only
|
||||
```
|
||||
|
||||
### PlanningCategory
|
||||
|
||||
```javascript
|
||||
PlanningCategory.Budget // Budget category
|
||||
PlanningCategory.Forecast // Forecast category
|
||||
PlanningCategory.Actual // Actual category
|
||||
PlanningCategory.Plan // Plan category
|
||||
```
|
||||
|
||||
### DataLockingState
|
||||
|
||||
```javascript
|
||||
DataLockingState.Locked // Locked by another user
|
||||
DataLockingState.Unlocked // Available for editing
|
||||
DataLockingState.LockedByMe // Locked by current user
|
||||
```
|
||||
|
||||
### ExecutionStatus
|
||||
|
||||
```javascript
|
||||
ExecutionStatus.Running // In progress
|
||||
ExecutionStatus.Completed // Successfully completed
|
||||
ExecutionStatus.Failed // Execution failed
|
||||
ExecutionStatus.Cancelled // Cancelled by user
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Examples
|
||||
|
||||
### Example 1: Version Publishing Workflow
|
||||
|
||||
```javascript
|
||||
// Button: Publish Budget
|
||||
Button_Publish.onClick = function() {
|
||||
Application.showBusyIndicator();
|
||||
|
||||
try {
|
||||
var budgetVersion = Table_1.getPlanning().getPublicVersion("Budget2024");
|
||||
|
||||
if (!budgetVersion) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Budget version not found"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!budgetVersion.isDirty()) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"No changes to publish"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
budgetVersion.publish();
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Success,
|
||||
"Budget published successfully"
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
console.log("Error:", error);
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Failed to publish budget"
|
||||
);
|
||||
} finally {
|
||||
Application.hideBusyIndicator();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Example 2: Create Working Copy
|
||||
|
||||
```javascript
|
||||
// Button: Create Working Copy
|
||||
Button_CreateCopy.onClick = function() {
|
||||
var sourceVersion = Table_1.getPlanning().getPublicVersion("Budget2024");
|
||||
|
||||
if (sourceVersion) {
|
||||
var timestamp = Date.now().toString();
|
||||
var copyName = "WorkingCopy_" + timestamp;
|
||||
|
||||
sourceVersion.copy(copyName, PlanningCopyOption.AllData);
|
||||
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"Created working copy: " + copyName
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Example 3: Data Action with Confirmation
|
||||
|
||||
```javascript
|
||||
// Button: Run Allocation
|
||||
Button_RunAllocation.onClick = function() {
|
||||
Popup_Confirm.open();
|
||||
};
|
||||
|
||||
// Confirm button in popup
|
||||
Button_ConfirmYes.onClick = function() {
|
||||
Popup_Confirm.close();
|
||||
|
||||
Application.showBusyIndicator();
|
||||
|
||||
// Set parameters from dropdown selections
|
||||
DataAction_Allocation.setParameterValue(
|
||||
"SOURCE_VERSION",
|
||||
Dropdown_SourceVersion.getSelectedKey()
|
||||
);
|
||||
DataAction_Allocation.setParameterValue(
|
||||
"TARGET_VERSION",
|
||||
Dropdown_TargetVersion.getSelectedKey()
|
||||
);
|
||||
DataAction_Allocation.setParameterValue(
|
||||
"YEAR",
|
||||
Dropdown_Year.getSelectedKey()
|
||||
);
|
||||
|
||||
DataAction_Allocation.execute();
|
||||
};
|
||||
|
||||
DataAction_Allocation.onAfterExecute = function(result) {
|
||||
Application.hideBusyIndicator();
|
||||
|
||||
if (result.success) {
|
||||
// Refresh data
|
||||
Table_1.getDataSource().refreshData();
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Success,
|
||||
"Allocation completed successfully"
|
||||
);
|
||||
} else {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Allocation failed: " + result.message
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Example 4: Find Active Planning Cycle
|
||||
|
||||
```javascript
|
||||
// Find active planning cycle from attribute
|
||||
function findActivePlanningCycle() {
|
||||
var allCycles = PlanningModel_1.getMembers("PlanningCycle");
|
||||
var activeCycle = null;
|
||||
|
||||
for (var i = 0; i < allCycles.length; i++) {
|
||||
if (allCycles[i].properties.IsActive === "X") {
|
||||
activeCycle = allCycles[i].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return activeCycle;
|
||||
}
|
||||
|
||||
// Use in initialization or filter setup
|
||||
var activeCycle = findActivePlanningCycle();
|
||||
if (activeCycle) {
|
||||
Table_1.getDataSource().setDimensionFilter(
|
||||
"PlanningCycle",
|
||||
activeCycle
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Example 5: Revert Changes
|
||||
|
||||
```javascript
|
||||
// Button: Discard Changes
|
||||
Button_Revert.onClick = function() {
|
||||
var version = Table_1.getPlanning().getPublicVersion("Budget2024");
|
||||
|
||||
if (version && version.isDirty()) {
|
||||
version.revert();
|
||||
Table_1.getDataSource().refreshData();
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"Changes discarded"
|
||||
);
|
||||
} else {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"No changes to discard"
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always check version exists** before operations
|
||||
2. **Use isDirty()** before publish to avoid unnecessary saves
|
||||
3. **Show busy indicator** during long operations
|
||||
4. **Handle errors** with try-catch blocks
|
||||
5. **Refresh data** after successful operations
|
||||
6. **Check lock state** before editing shared data
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [DataSource API](api-datasource.md)
|
||||
- [Widgets API](api-widgets.md)
|
||||
- [Application API](api-application.md)
|
||||
|
||||
**Official Reference**: [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
622
references/api-widgets.md
Normal file
622
references/api-widgets.md
Normal file
@@ -0,0 +1,622 @@
|
||||
# Widget APIs Reference
|
||||
|
||||
Complete reference for widget scripting APIs in SAP Analytics Cloud.
|
||||
|
||||
**Source**: [Analytics Designer API Reference 2025.14](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Common Widget Methods](#common-widget-methods)
|
||||
2. [Chart Widget](#chart-widget)
|
||||
3. [Table Widget](#table-widget)
|
||||
4. [Input Controls](#input-controls)
|
||||
5. [Button Widget](#button-widget)
|
||||
6. [Text Widget](#text-widget)
|
||||
7. [Image Widget](#image-widget)
|
||||
8. [GeoMap Widget](#geomap-widget)
|
||||
9. [Popup and Dialog](#popup-and-dialog)
|
||||
10. [Feed Constants](#feed-constants)
|
||||
|
||||
---
|
||||
|
||||
## Common Widget Methods
|
||||
|
||||
All widgets share these base methods:
|
||||
|
||||
### Visibility
|
||||
|
||||
```javascript
|
||||
Widget_1.setVisible(true); // Show widget
|
||||
Widget_1.setVisible(false); // Hide widget
|
||||
var isVisible = Widget_1.isVisible();
|
||||
```
|
||||
|
||||
### Enabled State
|
||||
|
||||
```javascript
|
||||
Widget_1.setEnabled(true); // Enable widget
|
||||
Widget_1.setEnabled(false); // Disable widget
|
||||
var isEnabled = Widget_1.isEnabled();
|
||||
```
|
||||
|
||||
### Styling
|
||||
|
||||
```javascript
|
||||
Widget_1.setCssClass("myCustomClass");
|
||||
var cssClass = Widget_1.getCssClass();
|
||||
```
|
||||
|
||||
### Focus
|
||||
|
||||
```javascript
|
||||
Widget_1.focus();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Chart Widget
|
||||
|
||||
### Data Source Access
|
||||
|
||||
```javascript
|
||||
var ds = Chart_1.getDataSource();
|
||||
```
|
||||
|
||||
### Dimension and Measure Management
|
||||
|
||||
#### addDimension(feed, dimensionId)
|
||||
|
||||
```javascript
|
||||
Chart_1.addDimension(Feed.CategoryAxis, "Location");
|
||||
Chart_1.addDimension(Feed.Color, "Product");
|
||||
```
|
||||
|
||||
#### removeDimension(feed, dimensionId)
|
||||
|
||||
```javascript
|
||||
Chart_1.removeDimension(Feed.CategoryAxis, "Location");
|
||||
```
|
||||
|
||||
#### addMeasure(feed, measureId, index?)
|
||||
|
||||
```javascript
|
||||
// Add measure to value axis
|
||||
Chart_1.addMeasure(Feed.ValueAxis, "[Account].[parentId].&[Revenue]");
|
||||
|
||||
// Add at specific position
|
||||
Chart_1.addMeasure(Feed.ValueAxis, "[Account].[parentId].&[Cost]", 1);
|
||||
```
|
||||
|
||||
#### removeMeasure(feed, measureId)
|
||||
|
||||
```javascript
|
||||
Chart_1.removeMeasure(Feed.ValueAxis, "[Account].[parentId].&[Revenue]");
|
||||
```
|
||||
|
||||
#### getMembers(feed)
|
||||
|
||||
```javascript
|
||||
var measures = Chart_1.getMembers(Feed.ValueAxis);
|
||||
var dimensions = Chart_1.getMembers(Feed.CategoryAxis);
|
||||
```
|
||||
|
||||
#### removeMember(feed, memberId)
|
||||
|
||||
```javascript
|
||||
var currentMeasures = Chart_1.getMembers(Feed.ValueAxis);
|
||||
Chart_1.removeMember(Feed.ValueAxis, currentMeasures[0]);
|
||||
```
|
||||
|
||||
### Selections
|
||||
|
||||
#### getSelections()
|
||||
|
||||
Returns user-selected data points.
|
||||
|
||||
```javascript
|
||||
var selections = Chart_1.getSelections();
|
||||
// Returns: Array of selection objects
|
||||
// Each object maps dimension ID to member ID
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
var selections = Chart_1.getSelections();
|
||||
if (selections.length > 0) {
|
||||
var selectedLocation = selections[0]["Location"];
|
||||
var selectedYear = selections[0]["Date"];
|
||||
console.log("Selected:", selectedLocation, selectedYear);
|
||||
}
|
||||
```
|
||||
|
||||
### Sorting
|
||||
|
||||
#### sortByValue(measureId, order)
|
||||
|
||||
```javascript
|
||||
Chart_1.sortByValue("[Account].[parentId].&[Revenue]", SortOrder.Descending);
|
||||
```
|
||||
|
||||
#### sortByMember(dimensionId, memberId, order)
|
||||
|
||||
```javascript
|
||||
Chart_1.sortByMember("Date", "[Date].[Year].&[2024]", SortOrder.Ascending);
|
||||
```
|
||||
|
||||
### Ranking
|
||||
|
||||
#### rankBy(measureId, count, order)
|
||||
|
||||
```javascript
|
||||
// Top 10 by revenue
|
||||
Chart_1.rankBy("[Account].[parentId].&[Revenue]", 10, SortOrder.Descending);
|
||||
|
||||
// Bottom 5
|
||||
Chart_1.rankBy("[Account].[parentId].&[Revenue]", 5, SortOrder.Ascending);
|
||||
```
|
||||
|
||||
### Formatting
|
||||
|
||||
#### getNumberFormat(measureId)
|
||||
|
||||
```javascript
|
||||
var format = Chart_1.getNumberFormat("[Account].[parentId].&[Revenue]");
|
||||
```
|
||||
|
||||
#### setNumberFormat(measureId, format)
|
||||
|
||||
```javascript
|
||||
Chart_1.setNumberFormat("[Account].[parentId].&[Revenue]", {
|
||||
decimalPlaces: 2,
|
||||
scalingFactor: 1000,
|
||||
showSign: true
|
||||
});
|
||||
```
|
||||
|
||||
### Chart Events
|
||||
|
||||
```javascript
|
||||
// onSelect - User selects data point
|
||||
Chart_1.onSelect = function() {
|
||||
var selections = Chart_1.getSelections();
|
||||
// Handle selection
|
||||
};
|
||||
|
||||
// onResultChanged - Data changes
|
||||
Chart_1.onResultChanged = function() {
|
||||
// Handle data change
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Table Widget
|
||||
|
||||
### Data Source Access
|
||||
|
||||
```javascript
|
||||
var ds = Table_1.getDataSource();
|
||||
```
|
||||
|
||||
### Dimension Management
|
||||
|
||||
#### addDimensionToRows(dimensionId)
|
||||
|
||||
```javascript
|
||||
Table_1.addDimensionToRows("Product");
|
||||
```
|
||||
|
||||
#### addDimensionToColumns(dimensionId)
|
||||
|
||||
```javascript
|
||||
Table_1.addDimensionToColumns("Date");
|
||||
```
|
||||
|
||||
#### removeDimensionFromRows(dimensionId)
|
||||
|
||||
```javascript
|
||||
Table_1.removeDimensionFromRows("Product");
|
||||
```
|
||||
|
||||
#### removeDimensionFromColumns(dimensionId)
|
||||
|
||||
```javascript
|
||||
Table_1.removeDimensionFromColumns("Date");
|
||||
```
|
||||
|
||||
### Display Options
|
||||
|
||||
#### setZeroSuppressionEnabled(enabled)
|
||||
|
||||
```javascript
|
||||
Table_1.setZeroSuppressionEnabled(true); // Hide zero rows
|
||||
```
|
||||
|
||||
#### setCompactDisplayEnabled(enabled)
|
||||
|
||||
```javascript
|
||||
Table_1.setCompactDisplayEnabled(true);
|
||||
```
|
||||
|
||||
### Selections
|
||||
|
||||
```javascript
|
||||
var selections = Table_1.getSelections();
|
||||
```
|
||||
|
||||
### Planning Access
|
||||
|
||||
```javascript
|
||||
var planning = Table_1.getPlanning();
|
||||
// See Planning API reference
|
||||
```
|
||||
|
||||
### Comments
|
||||
|
||||
```javascript
|
||||
var comments = Table_1.getComments();
|
||||
```
|
||||
|
||||
### Table Events
|
||||
|
||||
```javascript
|
||||
// onSelect - User selects cell
|
||||
Table_1.onSelect = function() {
|
||||
var selections = Table_1.getSelections();
|
||||
};
|
||||
|
||||
// onResultChanged - Data changes
|
||||
Table_1.onResultChanged = function() {
|
||||
// Handle data change
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Input Controls
|
||||
|
||||
### Dropdown
|
||||
|
||||
```javascript
|
||||
// Get selected value
|
||||
var value = Dropdown_1.getSelectedKey();
|
||||
|
||||
// Set selected value
|
||||
Dropdown_1.setSelectedKey("2024");
|
||||
|
||||
// Get all items
|
||||
var items = Dropdown_1.getItems();
|
||||
|
||||
// Set items
|
||||
Dropdown_1.setItems([
|
||||
{ key: "2023", text: "Year 2023" },
|
||||
{ key: "2024", text: "Year 2024" }
|
||||
]);
|
||||
```
|
||||
|
||||
**Events**:
|
||||
```javascript
|
||||
Dropdown_1.onSelect = function() {
|
||||
var selected = Dropdown_1.getSelectedKey();
|
||||
// Apply filter
|
||||
Chart_1.getDataSource().setDimensionFilter("Year", selected);
|
||||
};
|
||||
```
|
||||
|
||||
### ListBox
|
||||
|
||||
```javascript
|
||||
// Single selection
|
||||
var value = ListBox_1.getSelectedKey();
|
||||
|
||||
// Multiple selection
|
||||
var values = ListBox_1.getSelectedKeys();
|
||||
|
||||
// Set selection
|
||||
ListBox_1.setSelectedKey("item1");
|
||||
ListBox_1.setSelectedKeys(["item1", "item2"]);
|
||||
```
|
||||
|
||||
### CheckboxGroup
|
||||
|
||||
```javascript
|
||||
// Get checked items
|
||||
var checked = CheckboxGroup_1.getSelectedKeys();
|
||||
|
||||
// Set checked items
|
||||
CheckboxGroup_1.setSelectedKeys(["opt1", "opt3"]);
|
||||
```
|
||||
|
||||
### RadioButtonGroup
|
||||
|
||||
```javascript
|
||||
var selected = RadioButtonGroup_1.getSelectedKey();
|
||||
RadioButtonGroup_1.setSelectedKey("option2");
|
||||
```
|
||||
|
||||
### Slider
|
||||
|
||||
```javascript
|
||||
// Get value
|
||||
var value = Slider_1.getValue();
|
||||
|
||||
// Set value
|
||||
Slider_1.setValue(75);
|
||||
|
||||
// Set range
|
||||
Slider_1.setMin(0);
|
||||
Slider_1.setMax(100);
|
||||
Slider_1.setStep(5);
|
||||
```
|
||||
|
||||
### RangeSlider
|
||||
|
||||
```javascript
|
||||
// Get range values
|
||||
var startValue = RangeSlider_1.getStartValue();
|
||||
var endValue = RangeSlider_1.getEndValue();
|
||||
|
||||
// Set range
|
||||
RangeSlider_1.setStartValue(10);
|
||||
RangeSlider_1.setEndValue(90);
|
||||
```
|
||||
|
||||
### InputField
|
||||
|
||||
```javascript
|
||||
// Get/Set value
|
||||
var text = InputField_1.getValue();
|
||||
InputField_1.setValue("New text");
|
||||
|
||||
// Placeholder
|
||||
InputField_1.setPlaceholder("Enter value...");
|
||||
```
|
||||
|
||||
### Switch
|
||||
|
||||
```javascript
|
||||
// Get state
|
||||
var isOn = Switch_1.isSelected();
|
||||
|
||||
// Set state
|
||||
Switch_1.setSelected(true);
|
||||
```
|
||||
|
||||
### DatePicker
|
||||
|
||||
```javascript
|
||||
// Get selected date
|
||||
var date = DatePicker_1.getValue();
|
||||
|
||||
// Set date
|
||||
DatePicker_1.setValue(new Date(2024, 0, 1));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Button Widget
|
||||
|
||||
### Properties
|
||||
|
||||
```javascript
|
||||
// Set text
|
||||
Button_1.setText("Click Me");
|
||||
|
||||
// Get text
|
||||
var text = Button_1.getText();
|
||||
|
||||
// Set icon
|
||||
Button_1.setIcon("sap-icon://accept");
|
||||
```
|
||||
|
||||
### Events
|
||||
|
||||
```javascript
|
||||
Button_1.onClick = function() {
|
||||
// Handle click
|
||||
Application.showMessage(ApplicationMessageType.Info, "Button clicked!");
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Text Widget
|
||||
|
||||
```javascript
|
||||
// Set text
|
||||
Text_1.setText("Hello World");
|
||||
Text_1.setText("Value: " + myVariable);
|
||||
|
||||
// Get text
|
||||
var text = Text_1.getText();
|
||||
|
||||
// Apply styling
|
||||
Text_1.setCssClass("highlightText");
|
||||
```
|
||||
|
||||
### Dynamic Text with Placeholders
|
||||
|
||||
```javascript
|
||||
// Set text with bound values
|
||||
Text_1.setText("Revenue: " + revenue.formattedValue);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Image Widget
|
||||
|
||||
```javascript
|
||||
// Set image source
|
||||
Image_1.setSrc("[https://example.com/image.png](https://example.com/image.png)");
|
||||
|
||||
// From content network
|
||||
Image_1.setSrc("sap-icon://home");
|
||||
|
||||
// Alt text
|
||||
Image_1.setAlt("Company Logo");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GeoMap Widget
|
||||
|
||||
### Layer Access
|
||||
|
||||
```javascript
|
||||
var layer = GeoMap_1.getLayer("LayerName");
|
||||
var ds = layer.getDataSource();
|
||||
```
|
||||
|
||||
### Visibility
|
||||
|
||||
```javascript
|
||||
GeoMap_1.setLayerVisible("LayerName", true);
|
||||
GeoMap_1.setLayerVisible("LayerName", false);
|
||||
```
|
||||
|
||||
### Selection
|
||||
|
||||
```javascript
|
||||
var selections = GeoMap_1.getSelections();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Popup and Dialog
|
||||
|
||||
### Create and Open
|
||||
|
||||
```javascript
|
||||
// Open popup
|
||||
Popup_1.open();
|
||||
|
||||
// Close popup
|
||||
Popup_1.close();
|
||||
```
|
||||
|
||||
### Dialog Mode
|
||||
|
||||
Enable in Builder panel: "Enable header & footer"
|
||||
|
||||
```javascript
|
||||
// Dialog with custom buttons
|
||||
// Configure in Builder panel
|
||||
```
|
||||
|
||||
### Popup Events
|
||||
|
||||
```javascript
|
||||
Popup_1.onOpen = function() {
|
||||
// Popup opened
|
||||
};
|
||||
|
||||
Popup_1.onClose = function() {
|
||||
// Popup closed
|
||||
};
|
||||
```
|
||||
|
||||
### Example: Confirmation Dialog
|
||||
|
||||
```javascript
|
||||
// Button click opens popup
|
||||
Button_Confirm.onClick = function() {
|
||||
Popup_Confirm.open();
|
||||
};
|
||||
|
||||
// Confirm button in popup
|
||||
Button_Yes.onClick = function() {
|
||||
// Perform action
|
||||
Table_1.getPlanning().getPublicVersion("Budget").publish();
|
||||
Popup_Confirm.close();
|
||||
};
|
||||
|
||||
// Cancel button
|
||||
Button_No.onClick = function() {
|
||||
Popup_Confirm.close();
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Feed Constants
|
||||
|
||||
Use with Chart dimension/measure methods:
|
||||
|
||||
```javascript
|
||||
Feed.CategoryAxis // X-axis dimension
|
||||
Feed.Color // Color/legend dimension
|
||||
Feed.ValueAxis // Y-axis measures
|
||||
Feed.ValueAxis2 // Secondary Y-axis
|
||||
Feed.Size // Bubble size (bubble charts)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SortOrder Enumeration
|
||||
|
||||
```javascript
|
||||
SortOrder.Ascending // A-Z, 0-9
|
||||
SortOrder.Descending // Z-A, 9-0
|
||||
SortOrder.Default // Default order
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Example: Interactive Dashboard
|
||||
|
||||
```javascript
|
||||
// Dropdown filter changes chart and table
|
||||
Dropdown_Year.onSelect = function() {
|
||||
var year = Dropdown_Year.getSelectedKey();
|
||||
|
||||
Application.showBusyIndicator();
|
||||
|
||||
// Pause refreshes
|
||||
Chart_1.getDataSource().setRefreshPaused(true);
|
||||
Table_1.getDataSource().setRefreshPaused(true);
|
||||
|
||||
// Apply filter to both
|
||||
Chart_1.getDataSource().setDimensionFilter("Year", year);
|
||||
Table_1.getDataSource().setDimensionFilter("Year", year);
|
||||
|
||||
// Update title
|
||||
Text_Title.setText("Sales Report - " + year);
|
||||
|
||||
// Resume refreshes
|
||||
Chart_1.getDataSource().setRefreshPaused(false);
|
||||
Table_1.getDataSource().setRefreshPaused(false);
|
||||
|
||||
Application.hideBusyIndicator();
|
||||
};
|
||||
|
||||
// Chart selection filters table
|
||||
Chart_1.onSelect = function() {
|
||||
var selections = Chart_1.getSelections();
|
||||
if (selections.length > 0) {
|
||||
var region = selections[0]["Region"];
|
||||
Table_1.getDataSource().setDimensionFilter("Region", region);
|
||||
}
|
||||
};
|
||||
|
||||
// Reset button
|
||||
Button_Reset.onClick = function() {
|
||||
Chart_1.getDataSource().removeDimensionFilter("Year");
|
||||
Chart_1.getDataSource().removeDimensionFilter("Region");
|
||||
Table_1.getDataSource().removeDimensionFilter("Year");
|
||||
Table_1.getDataSource().removeDimensionFilter("Region");
|
||||
Dropdown_Year.setSelectedKey("");
|
||||
Text_Title.setText("Sales Report - All Years");
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [DataSource API](api-datasource.md)
|
||||
- [Planning API](api-planning.md)
|
||||
- [Application API](api-application.md)
|
||||
|
||||
**Official Reference**: [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
16
references/auth-required.md
Normal file
16
references/auth-required.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Authentication-Required Page (2025.23)
|
||||
|
||||
Source: `help-portal-8a2db072d3a8463899b7224563d787d2.md` (Authentication Required).
|
||||
|
||||
## Status
|
||||
- ❌ Page requires SAP ID authentication; scraping redirected to SAP login.
|
||||
|
||||
## URLs
|
||||
- Original: `.../8a2db072d3a8463899b7224563d787d2.html?locale=en-US&version=2025.23`
|
||||
- Redirect: `[https://accounts.sap.com/saml2/idp/sso/accounts.sap.com`](https://accounts.sap.com/saml2/idp/sso/accounts.sap.com`)
|
||||
|
||||
## Recommendations
|
||||
1. Access via authenticated SAP Help Portal account.
|
||||
2. Look for equivalent public documentation if available.
|
||||
3. Check other Help Portal pages for similar content that is publicly accessible.
|
||||
|
||||
15
references/automatic-refactoring.md
Normal file
15
references/automatic-refactoring.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Automatic Refactoring (2025.23)
|
||||
|
||||
Source: `help-portal-da7f92f6a2ac4ab9b5498da68a82712b.md`.
|
||||
|
||||
## Overview
|
||||
Built-in refactoring helps keep scripts consistent and maintainable.
|
||||
|
||||
## Capabilities
|
||||
- Rename identifiers across scripts.
|
||||
- Update references safely when moving/renaming objects.
|
||||
|
||||
## Notes
|
||||
- Use refactor tools instead of manual search/replace to avoid broken references.
|
||||
- Verify changes in value help after refactoring.
|
||||
|
||||
304
references/best-practices-developer.md
Normal file
304
references/best-practices-developer.md
Normal file
@@ -0,0 +1,304 @@
|
||||
# Developer Best Practices for Maintainable SAC Stories
|
||||
|
||||
Best practices for building SAC stories that are scalable, maintainable, and developer-friendly.
|
||||
|
||||
**Source**: [Building Stories That Other Developers Actually Want to Inherit](https://community.sap.com/t5/technology-blog-posts-by-members/building-stories-that-other-developers-actually-want-to-inherit/ba-p/14168133) by JBARLOW (SAP Community, July 2025)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Widget Naming Conventions](#widget-naming-conventions)
|
||||
2. [Layout Organization](#layout-organization)
|
||||
3. [Script Annotation Standards](#script-annotation-standards)
|
||||
4. [Design Tips](#design-tips)
|
||||
5. [Summary](#summary)
|
||||
|
||||
---
|
||||
|
||||
## Widget Naming Conventions
|
||||
|
||||
The most impactful practice for maintainable SAC stories is consistent widget naming.
|
||||
|
||||
### Standard Prefixes
|
||||
|
||||
| Widget Type | Prefix | Example |
|
||||
|-------------|--------|---------|
|
||||
| Bar Chart | `chartB_` | `chartB_revenue_by_state` |
|
||||
| Line Chart | `chartL_` | `chartL_margin_by_product` |
|
||||
| Numeric Point Chart | `kpi_` | `kpi_actuals_vs_budget` |
|
||||
| Waterfall Chart | `chartWF_` | `chartWF_spend` |
|
||||
| Table | `tbl_` | `tbl_invoices_by_month` |
|
||||
| Panel | `pnl_` | `pnl_title` |
|
||||
| Text Box | `txt_` | `txt_title` |
|
||||
| Image | `img_` | `img_logo` |
|
||||
| Shape | `shape_` | `shape_header_divider` |
|
||||
| Button | `btn_` | `btn_page2` |
|
||||
| Dropdown List | `ddl_` | `ddl_product` |
|
||||
| List Box | `lb_` | `lb_store` |
|
||||
| Radio Button Group | `rbg_` | `rbg_managers` |
|
||||
| Checkbox | `chk_` | `chk_include_forecast` |
|
||||
|
||||
### Why Naming Matters
|
||||
|
||||
**Without naming conventions** (Bad):
|
||||
```
|
||||
Outline View:
|
||||
├── Chart
|
||||
├── Chart_1
|
||||
├── Chart_2
|
||||
├── Table
|
||||
├── Table_1
|
||||
├── Button
|
||||
├── Button_1
|
||||
```
|
||||
|
||||
**With naming conventions** (Good):
|
||||
```
|
||||
Outline View:
|
||||
├── chartB_revenue_by_region
|
||||
├── chartL_revenue_trend
|
||||
├── chartWF_budget_variance
|
||||
├── tbl_detailed_transactions
|
||||
├── tbl_summary_by_product
|
||||
├── btn_export_pdf
|
||||
├── btn_reset_filters
|
||||
```
|
||||
|
||||
### Benefits
|
||||
|
||||
1. **Autocomplete clarity**: When typing in script editor, CTRL+Space shows meaningful names
|
||||
2. **Easier debugging**: Quickly identify which widget is referenced in error messages
|
||||
3. **Team collaboration**: Other developers immediately understand widget purposes
|
||||
4. **Self-documenting**: Code becomes readable without additional comments
|
||||
|
||||
### Implementation Guidelines
|
||||
|
||||
- Include naming conventions in organization's design guidelines
|
||||
- Apply consistently from project start
|
||||
- Don't get lazy and stop mid-project
|
||||
- Use lowercase with underscores for readability
|
||||
|
||||
---
|
||||
|
||||
## Layout Organization
|
||||
|
||||
Use panels to group related content for easier navigation and maintenance.
|
||||
|
||||
### Panel-Based Structure
|
||||
|
||||
```
|
||||
Story Layout:
|
||||
├── pnl_header
|
||||
│ ├── img_logo
|
||||
│ ├── txt_title
|
||||
│ └── shape_header_divider
|
||||
├── pnl_filters
|
||||
│ ├── ddl_year
|
||||
│ ├── ddl_region
|
||||
│ └── btn_apply_filters
|
||||
├── pnl_kpis
|
||||
│ ├── kpi_revenue
|
||||
│ ├── kpi_margin
|
||||
│ └── kpi_growth
|
||||
├── pnl_main_chart
|
||||
│ └── chartB_revenue_by_product
|
||||
└── pnl_details
|
||||
└── tbl_transactions
|
||||
```
|
||||
|
||||
### Ordering Convention
|
||||
|
||||
Order widgets in Outline view to match visual layout:
|
||||
- **Top to bottom**
|
||||
- **Left to right**
|
||||
|
||||
This allows developers to understand layout without viewing the canvas.
|
||||
|
||||
### Benefits of Panels
|
||||
|
||||
1. **Quick navigation**: Find content blocks instantly in Outline
|
||||
2. **Easy repositioning**: Move entire sections by dragging one panel
|
||||
3. **Simplified scripting**: Show/hide panel instead of multiple widgets
|
||||
4. **Performance**: No significant performance impact from using many panels
|
||||
|
||||
### Show/Hide Pattern
|
||||
|
||||
```javascript
|
||||
// Instead of hiding multiple widgets individually
|
||||
// AVOID:
|
||||
Chart_1.setVisible(false);
|
||||
Table_1.setVisible(false);
|
||||
Text_1.setVisible(false);
|
||||
Button_1.setVisible(false);
|
||||
|
||||
// Hide the containing panel
|
||||
// GOOD:
|
||||
pnl_details.setVisible(false);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Script Annotation Standards
|
||||
|
||||
Well-documented scripts dramatically reduce maintenance friction.
|
||||
|
||||
### Summary-Level Comments
|
||||
|
||||
Add a summary block at the start of each script explaining:
|
||||
- **Purpose**: What this script accomplishes
|
||||
- **Interaction**: How it connects to other scripts
|
||||
- **Dependencies**: Widgets, variables, or data sources referenced
|
||||
|
||||
```javascript
|
||||
/*
|
||||
* ============================================
|
||||
* Script: onSelect - chartB_revenue_by_region
|
||||
* ============================================
|
||||
* Purpose: Filter detail table when user selects a region in the chart
|
||||
*
|
||||
* Interaction:
|
||||
* - Reads: chartB_revenue_by_region selections
|
||||
* - Updates: tbl_transactions filter
|
||||
* - Triggers: onResultChanged on tbl_transactions
|
||||
*
|
||||
* Dependencies:
|
||||
* - chartB_revenue_by_region (Chart widget)
|
||||
* - tbl_transactions (Table widget)
|
||||
* - GlobalVar_selectedRegion (String variable)
|
||||
* ============================================
|
||||
*/
|
||||
|
||||
// Script implementation below...
|
||||
```
|
||||
|
||||
### Line-Level Comments
|
||||
|
||||
Add inline comments explaining non-obvious logic:
|
||||
|
||||
```javascript
|
||||
// Get user's selection from the chart
|
||||
var selections = chartB_revenue_by_region.getSelections();
|
||||
|
||||
// Guard clause: exit if nothing selected
|
||||
if (selections.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract region ID from selection object
|
||||
// Note: Key name matches dimension ID in model
|
||||
var selectedRegion = selections[0]["Region"];
|
||||
|
||||
// Store in global variable for other scripts to access
|
||||
GlobalVar_selectedRegion = selectedRegion;
|
||||
|
||||
// Apply filter to detail table
|
||||
// Using setDimensionFilter to replace any existing filter
|
||||
tbl_transactions.getDataSource().setDimensionFilter("Region", selectedRegion);
|
||||
```
|
||||
|
||||
### AI-Assisted Annotation
|
||||
|
||||
For existing undocumented scripts:
|
||||
1. Copy script to AI assistant (ChatGPT, Claude, etc.)
|
||||
2. Request annotation and summary generation
|
||||
3. **Always review and verify** the AI output for accuracy
|
||||
4. Adjust terminology to match your project conventions
|
||||
|
||||
---
|
||||
|
||||
## Design Tips
|
||||
|
||||
### Panel Auto Scroll
|
||||
|
||||
**Recommendation**: Turn OFF
|
||||
|
||||
- Prevents unnecessary scroll bars
|
||||
- Provides hard boundaries for widget positioning
|
||||
- Makes layout arrangement predictable
|
||||
|
||||
```
|
||||
Panel Settings → Auto Scroll → Disabled
|
||||
```
|
||||
|
||||
### Flow Panels
|
||||
|
||||
**Recommendation**: Avoid
|
||||
|
||||
- Flow panels make precise positioning difficult
|
||||
- Fixed layouts are easier to maintain
|
||||
- Plan your layout before building; flow panels indicate unclear requirements
|
||||
|
||||
### Dynamic/Responsive Sizing
|
||||
|
||||
Build stories that adapt to varying screen sizes:
|
||||
|
||||
**Development Workflow**:
|
||||
1. Build with **auto widget widths** and **fixed px** Left/Right values
|
||||
2. Test and finalize layout
|
||||
3. Change Left/Right from **px** to **%** for responsiveness
|
||||
|
||||
```javascript
|
||||
// Example: Widget positioning
|
||||
// Fixed (initial development):
|
||||
// Left: 20px, Right: 20px
|
||||
|
||||
// Responsive (after layout finalized):
|
||||
// Left: 2%, Right: 2%
|
||||
```
|
||||
|
||||
**Advanced Responsive Design**:
|
||||
- Use CSS classes for font size adjustments based on widget size
|
||||
- Consider breakpoints for significantly different screen sizes
|
||||
- Test on target devices before deployment
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Building maintainable SAC stories requires discipline in four areas:
|
||||
|
||||
| Area | Key Practice | Impact |
|
||||
|------|--------------|--------|
|
||||
| **Naming** | Use consistent prefixes for all widgets | Immediate clarity in scripts and Outline |
|
||||
| **Layout** | Group content in panels, order logically | Easy navigation and repositioning |
|
||||
| **Annotation** | Summary + line comments for all scripts | Reduced onboarding time for new developers |
|
||||
| **Design** | Disable auto scroll, use % sizing | Predictable, responsive layouts |
|
||||
|
||||
> *"Good design isn't just for end users—it starts with the developer."*
|
||||
> — JBARLOW, SAP Community
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Card
|
||||
|
||||
### Naming Prefixes
|
||||
```
|
||||
chartB_ chartL_ chartWF_ kpi_ tbl_ pnl_
|
||||
txt_ img_ shape_ btn_ ddl_ lb_ rbg_ chk_
|
||||
```
|
||||
|
||||
### Panel Visibility Pattern
|
||||
```javascript
|
||||
pnl_section.setVisible(true/false); // Show/hide entire section
|
||||
```
|
||||
|
||||
### Script Header Template
|
||||
```javascript
|
||||
/*
|
||||
* Script: [event] - [widget_name]
|
||||
* Purpose: [description]
|
||||
* Dependencies: [list]
|
||||
*/
|
||||
```
|
||||
|
||||
### Responsive Sizing
|
||||
```
|
||||
Development: px values → Production: % values
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**License**: GPL-3.0
|
||||
**Last Updated**: 2025-11-22
|
||||
**Repository**: [https://github.com/secondsky/sap-skills](https://github.com/secondsky/sap-skills)
|
||||
367
references/best-practices-planning-stories.md
Normal file
367
references/best-practices-planning-stories.md
Normal file
@@ -0,0 +1,367 @@
|
||||
# Best Practices for Planning Stories in SAP Analytics Cloud
|
||||
|
||||
Architectural patterns and user experience guidelines for building professional planning applications with multi-story navigation.
|
||||
|
||||
**Source**: [SAP PRESS Blog - Best Practices for Planning Stories in SAP Analytics Cloud](https://blog.sap-press.com/best-practices-for-planning-stories-in-sap-analytics-cloud)
|
||||
**Book**: *Application Development with SAP Analytics Cloud* by Josef Hampp and Jan Lang (SAP PRESS, 2024)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Entry Point Design](#entry-point-design)
|
||||
2. [Multi-Story Architecture](#multi-story-architecture)
|
||||
3. [Story Navigation via Scripting](#story-navigation-via-scripting)
|
||||
4. [User Assistance Patterns](#user-assistance-patterns)
|
||||
5. [Guided Process Implementation](#guided-process-implementation)
|
||||
6. [Button Design Guidelines](#button-design-guidelines)
|
||||
|
||||
---
|
||||
|
||||
## Entry Point Design
|
||||
|
||||
Create an overview/start page as the single entry point for users into the planning process.
|
||||
|
||||
### Structure
|
||||
|
||||
A well-designed entry page should be **structured and not overloaded**:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ KPI TILES (Top Section) │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||||
│ │ Metric1 │ │ Metric2 │ │ Metric3 │ │ Metric4 │ │
|
||||
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ NAVIGATION SECTIONS (Visually Separated) │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ ┌────────────┐│
|
||||
│ │ Configure App │ │ Plan FTE │ │ Reports ││
|
||||
│ │ & Parameters │ │ Demands & Costs │ │ ││
|
||||
│ │ │ │ │ │ ││
|
||||
│ │ > Settings │ │ > Workforce │ │ > Summary ││
|
||||
│ │ > Parameters │ │ > Budget │ │ > Details ││
|
||||
│ └─────────────────┘ └─────────────────┘ └────────────┘│
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Key Elements
|
||||
|
||||
| Element | Purpose |
|
||||
|---------|---------|
|
||||
| KPI Tiles | Display most important key figures at a glance |
|
||||
| Navigation Areas | Group related planning functions (3-4 max) |
|
||||
| Jump Links | Text fields with onClick scripts to navigate to sub-stories |
|
||||
| Visual Separation | Clear boundaries between functional areas |
|
||||
|
||||
### Benefits
|
||||
|
||||
- Provides orientation for users in complex planning processes
|
||||
- Reduces cognitive load with organized navigation
|
||||
- Enables easy access to all planning phases from one location
|
||||
|
||||
---
|
||||
|
||||
## Multi-Story Architecture
|
||||
|
||||
Separate individual planning steps into distinct stories for simplified maintenance.
|
||||
|
||||
### Folder Organization
|
||||
|
||||
Store all related stories in a dedicated folder structure:
|
||||
|
||||
```
|
||||
SAC File System/
|
||||
└── Planning_Application_2024/
|
||||
├── 00_Entry_Point.story
|
||||
├── 01_Application_Configuration.story
|
||||
├── 02_Plan_FTE_Demands.story
|
||||
├── 03_Plan_Costs.story
|
||||
├── 04_Summary_Report.story
|
||||
└── 05_Detail_Report.story
|
||||
```
|
||||
|
||||
### Benefits
|
||||
|
||||
| Benefit | Description |
|
||||
|---------|-------------|
|
||||
| **Easier Maintenance** | Update one story without affecting others |
|
||||
| **Team Collaboration** | Multiple developers can work on different stories |
|
||||
| **Version Control** | Track changes at story level |
|
||||
| **Performance** | Smaller stories load faster |
|
||||
| **Testing** | Test individual planning steps independently |
|
||||
|
||||
### Implementation Guidelines
|
||||
|
||||
1. Create one story per planning phase or major step
|
||||
2. Use consistent naming with numeric prefixes for ordering
|
||||
3. Share data sources across stories using the same model
|
||||
4. Implement consistent navigation patterns across all stories
|
||||
|
||||
---
|
||||
|
||||
## Story Navigation via Scripting
|
||||
|
||||
Use scripting instead of standard hyperlinks for cross-story navigation with URL parameters.
|
||||
|
||||
### Full Navigation Script
|
||||
|
||||
Add this script to a Script Object for reuse across your entry point:
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Opens a target story with specified display mode and page
|
||||
* @param {string} arg_StoryIdPageIndex - Format: "storyId|pageIndex"
|
||||
* @param {string} arg_displayMode - "view" or "edit"
|
||||
*/
|
||||
function navigateToStory(arg_StoryIdPageIndex, arg_displayMode) {
|
||||
var delimiter_storyIdPageIndex = "|";
|
||||
|
||||
// Extract story ID and page index from the combination provided
|
||||
var storyId = arg_StoryIdPageIndex.substring(
|
||||
0,
|
||||
arg_StoryIdPageIndex.indexOf(delimiter_storyIdPageIndex)
|
||||
);
|
||||
var pageIndex = arg_StoryIdPageIndex.substring(
|
||||
arg_StoryIdPageIndex.indexOf(delimiter_storyIdPageIndex) + 1
|
||||
);
|
||||
|
||||
// Create and fill array of URL parameters
|
||||
var urlParameters = ArrayUtils.create(Type.UrlParameter);
|
||||
urlParameters.push(UrlParameter.create("mode", arg_displayMode));
|
||||
urlParameters.push(UrlParameter.create("page", pageIndex));
|
||||
|
||||
// Optional: Add custom URL parameters
|
||||
// urlParameters.push(UrlParameter.create("p_myParam", "value"));
|
||||
|
||||
// Open the target story
|
||||
NavigationUtils.openStory(storyId, "", urlParameters, false);
|
||||
}
|
||||
```
|
||||
|
||||
### Usage in onClick Event
|
||||
|
||||
```javascript
|
||||
// onClick of txt_navigate_to_workforce
|
||||
// Story ID from the target story's URL, page index 0-based
|
||||
ScriptObject_Navigation.navigateToStory("STORY123ABC|0", "view");
|
||||
```
|
||||
|
||||
### Storing Story IDs
|
||||
|
||||
Create a Script Object to store story IDs for easy maintenance:
|
||||
|
||||
```javascript
|
||||
// ScriptObject_StoryRegistry
|
||||
var STORIES = {
|
||||
CONFIGURATION: "ABC123DEF|0",
|
||||
WORKFORCE_PLAN: "GHI456JKL|0",
|
||||
COST_PLAN: "MNO789PQR|0",
|
||||
SUMMARY_REPORT: "STU012VWX|1",
|
||||
DETAIL_REPORT: "YZA345BCD|0"
|
||||
};
|
||||
|
||||
function getStoryReference(storyName) {
|
||||
return STORIES[storyName];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## User Assistance Patterns
|
||||
|
||||
Implement consistent help features across all planning stories.
|
||||
|
||||
### Sidebar Structure
|
||||
|
||||
Include a sidebar on every story (except entry page) with:
|
||||
|
||||
```
|
||||
┌──────────────────────┐
|
||||
│ FILTERS │
|
||||
│ ├─ Year: [2024 ▼] │
|
||||
│ ├─ Region: [All ▼] │
|
||||
│ └─ [Apply Filters] │
|
||||
├──────────────────────┤
|
||||
│ NAVIGATION │
|
||||
│ ├─ ← Back to Entry │
|
||||
│ ├─ → Next Step │
|
||||
│ └─ ↺ Home │
|
||||
├──────────────────────┤
|
||||
│ INSTRUCTIONS │
|
||||
│ Step 1: Select... │
|
||||
│ Step 2: Enter... │
|
||||
│ Step 3: Save... │
|
||||
├──────────────────────┤
|
||||
│ EXTERNAL LINKS │
|
||||
│ ├─ 📖 Documentation │
|
||||
│ ├─ ❓ Ask Question │
|
||||
│ └─ ℹ️ Process Info │
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
### Step-by-Step Instructions
|
||||
|
||||
Provide clear instructions directly in the sidebar:
|
||||
|
||||
```
|
||||
INSTRUCTIONS
|
||||
────────────────────
|
||||
Step 1: Select the planning version
|
||||
from the dropdown above
|
||||
|
||||
Step 2: Enter FTE values in the table
|
||||
for each cost center
|
||||
|
||||
Step 3: Review the calculated costs
|
||||
in the summary section
|
||||
|
||||
Step 4: Click "Save" to store your
|
||||
changes
|
||||
|
||||
Step 5: Click "Submit" when ready
|
||||
for approval
|
||||
```
|
||||
|
||||
### Benefits
|
||||
|
||||
- Ensures consistent user experience across all stories
|
||||
- Reduces support requests with inline help
|
||||
- Enables occasional users to complete planning correctly
|
||||
|
||||
---
|
||||
|
||||
## Guided Process Implementation
|
||||
|
||||
Implement a "Guide Me!" button that opens a focused popup with step-by-step guidance.
|
||||
|
||||
### Popup Design
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ ✨ Planning Guide X│
|
||||
├─────────────────────────────────────┤
|
||||
│ │
|
||||
│ Welcome to Workforce Planning! │
|
||||
│ │
|
||||
│ Follow these steps: │
|
||||
│ │
|
||||
│ ☐ 1. Select your cost center │
|
||||
│ ☐ 2. Choose the planning period │
|
||||
│ ☐ 3. Enter FTE values │
|
||||
│ ☐ 4. Review calculated costs │
|
||||
│ ☐ 5. Submit for approval │
|
||||
│ │
|
||||
│ ┌─────────┐ ┌─────────────────┐ │
|
||||
│ │ Close │ │ Start Planning │ │
|
||||
│ └─────────┘ └─────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Implementation
|
||||
|
||||
1. Create a Popup widget with header enabled
|
||||
2. Add text widgets with step instructions
|
||||
3. Add navigation buttons
|
||||
4. Trigger popup from "Guide Me!" button:
|
||||
|
||||
```javascript
|
||||
// onClick of btn_guide_me
|
||||
Popup_Guide.open();
|
||||
```
|
||||
|
||||
### Dynamic Step Highlighting (Advanced)
|
||||
|
||||
```javascript
|
||||
// Track current step in global variable
|
||||
// GlobalVar_currentStep (Integer)
|
||||
|
||||
function highlightCurrentStep() {
|
||||
// Reset all step indicators
|
||||
txt_step1.setStyle("opacity", "0.5");
|
||||
txt_step2.setStyle("opacity", "0.5");
|
||||
txt_step3.setStyle("opacity", "0.5");
|
||||
|
||||
// Highlight current step
|
||||
switch (GlobalVar_currentStep) {
|
||||
case 1:
|
||||
txt_step1.setStyle("opacity", "1.0");
|
||||
break;
|
||||
case 2:
|
||||
txt_step2.setStyle("opacity", "1.0");
|
||||
break;
|
||||
case 3:
|
||||
txt_step3.setStyle("opacity", "1.0");
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Button Design Guidelines
|
||||
|
||||
Use color coding to make button functions immediately clear to users.
|
||||
|
||||
### Color Conventions
|
||||
|
||||
| Color | Usage | Examples |
|
||||
|-------|-------|----------|
|
||||
| **Green** | Positive/Confirm actions | Save, Submit, Approve, Publish |
|
||||
| **Red** | Negative/Destructive actions | Delete, Reject, Revert, Cancel |
|
||||
| **Blue** | Navigation/Neutral actions | Next, Back, View, Export |
|
||||
| **Gray** | Secondary/Disabled actions | Reset, Clear, Close |
|
||||
|
||||
### Button Layout Example
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ ACTION BUTTONS │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ 🔴 Reset │ │ 🔵 Export │ │ 🟢 Save │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Implementation Tips
|
||||
|
||||
1. Use consistent button sizing across the application
|
||||
2. Group related buttons together
|
||||
3. Place primary action (usually green) on the right
|
||||
4. Add confirmation dialogs for destructive actions:
|
||||
|
||||
```javascript
|
||||
// onClick of btn_delete
|
||||
var confirmed = Application.showConfirmDialog(
|
||||
"Confirm Delete",
|
||||
"Are you sure you want to delete this version?",
|
||||
"Delete",
|
||||
"Cancel"
|
||||
);
|
||||
|
||||
if (confirmed) {
|
||||
// Perform delete operation
|
||||
planning.getPrivateVersion("draft").delete();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Best Practice | Key Takeaway |
|
||||
|---------------|--------------|
|
||||
| **Entry Point** | Single overview page with KPIs and organized navigation |
|
||||
| **Multi-Story** | Separate stories per planning phase, stored in folders |
|
||||
| **Navigation** | Use scripting with `NavigationUtils.openStory()` |
|
||||
| **User Assistance** | Consistent sidebar with filters, instructions, links |
|
||||
| **Guided Process** | "Guide Me!" popup with step-by-step workflow |
|
||||
| **Buttons** | Color-coded: Green=positive, Red=negative, Blue=neutral |
|
||||
|
||||
---
|
||||
|
||||
**License**: GPL-3.0
|
||||
**Last Updated**: 2025-11-22
|
||||
**Repository**: [https://github.com/secondsky/sap-skills](https://github.com/secondsky/sap-skills)
|
||||
16
references/bind-widget-values.md
Normal file
16
references/bind-widget-values.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Bind Widget Values to Variables (2025.23)
|
||||
|
||||
Source: `help-portal-0192dc472ba946989d05d444e598e265.md`.
|
||||
|
||||
## Overview
|
||||
Bind widget values (for example, input controls) to variables for two-way data flow.
|
||||
|
||||
## Steps
|
||||
1. Select widget → Bind value to variable.
|
||||
2. Use script to read/write the bound variable.
|
||||
3. Variable changes propagate to widget and vice versa.
|
||||
|
||||
## Notes
|
||||
- Ensure variable types align with widget value types.
|
||||
- Useful for centralized state management across widgets.
|
||||
|
||||
26
references/blending-limitations.md
Normal file
26
references/blending-limitations.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Blending Limitations (2025.23)
|
||||
|
||||
Source: `help-portal-77eb1188969e4e0ea7dfe92b73e67ee2.md` (Blend Data in Analytic Applications).
|
||||
|
||||
## General restrictions
|
||||
- All story restrictions also apply to analytic applications.
|
||||
- Analytics Designer specifics: filter line, data point comment, navigation panel not supported.
|
||||
|
||||
## Scripting considerations
|
||||
- APIs/value help only operate on the **primary** model of blended charts/tables.
|
||||
- Dimension/measure member IDs include modelId in blended scenarios.
|
||||
- Calculated measures use UUID (no modelId); value help for blended charts returns no modelId.
|
||||
- Log chart/table selections in console to capture full IDs.
|
||||
|
||||
## Unsupported APIs for blending
|
||||
- **Planning APIs**: all.
|
||||
- **Navigation panel APIs**: all.
|
||||
- **Comment APIs**: all.
|
||||
- **Data explorer APIs**: all.
|
||||
- **Filter APIs**: `Chart.getDataSource().setDimensionFilter()`, `.removeDimensionFilter()`, `DataSource.copyDimensionFilterFrom()`, `DataSource.copyVariableValueFrom()`.
|
||||
- **Table APIs**: `Table.sortByMember()`, `sortByValue()`, `removeSorting()`, `rankBy()`, `removeRanking()`, `setBreakGroupingEnabled()`.
|
||||
|
||||
## Implementation notes
|
||||
- Always confirm dimension IDs when reusing API outputs.
|
||||
- Test carefully—many operations are unsupported in blended mode.
|
||||
|
||||
11
references/bookmark-set-tech-object.md
Normal file
11
references/bookmark-set-tech-object.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Bookmark Set Technical Object (2025.23)
|
||||
|
||||
Source: `help-portal-6f64b8234dc448c6b427f7df4a39ec4e.md`.
|
||||
|
||||
## Purpose
|
||||
Technical object to manage bookmark sets programmatically.
|
||||
|
||||
## Notes
|
||||
- Create bookmark sets, organize and apply saved application states.
|
||||
- Integrate with bookmark APIs and Linked Analysis strategies.
|
||||
|
||||
15
references/bookmark-settings.md
Normal file
15
references/bookmark-settings.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Bookmark Settings & APIs (2025.23)
|
||||
|
||||
Source: `help-portal-51e76005e0ee45e1a3b9a96271f11509.md`.
|
||||
|
||||
## Overview
|
||||
Configure and script bookmark behavior in analytic applications.
|
||||
|
||||
## Capabilities
|
||||
- Save and restore application state (global/personal).
|
||||
- Control bookmark visibility and defaults.
|
||||
- Programmatically apply bookmarks via API.
|
||||
|
||||
## Notes
|
||||
- Align bookmark strategy with Linked Analysis and filter design.
|
||||
|
||||
14
references/calendar-integration-tech-object.md
Normal file
14
references/calendar-integration-tech-object.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Calendar Integration Technical Object (2025.23)
|
||||
|
||||
Source: `help-portal-a727e860122944b0be6a8351456174f8.md`.
|
||||
|
||||
## Purpose
|
||||
Programmatic access to calendar tasks, processes, and workflows from analytic applications.
|
||||
|
||||
## Capabilities
|
||||
- Create, update, and manage calendar items.
|
||||
- Integrate planning workflows (submit/approve/reject) with scripts.
|
||||
|
||||
## Notes
|
||||
- Align with planning calendar permissions and scheduling rules.
|
||||
|
||||
14
references/check-errors.md
Normal file
14
references/check-errors.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Check Errors in Scripting (2025.23)
|
||||
|
||||
Source: `help-portal-d57d183a83f247e390c562719a42de51.md`.
|
||||
|
||||
## Errors panel
|
||||
1. Toolbar → Views → **Info Panel** → **Errors**.
|
||||
2. Filter by widget name; toggle errors vs warnings; expand/collapse all.
|
||||
3. Click an entry to jump to script with highlight.
|
||||
4. Fixed items disappear automatically; close via Info Panel or panel close icon.
|
||||
|
||||
## Notes
|
||||
- Use before runtime to catch missing references/typos.
|
||||
- Complements browser-console debugging.
|
||||
|
||||
15
references/check-references.md
Normal file
15
references/check-references.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Check References in Scripting (2025.23)
|
||||
|
||||
Source: `help-portal-ad9735ccc415440cb440060fac9d3500.md`.
|
||||
|
||||
## Purpose
|
||||
Identify missing or broken references across scripts and widgets.
|
||||
|
||||
## Usage
|
||||
- Use the reference checking tool in the script editor/IDE to list unresolved references.
|
||||
- Navigate directly from findings to affected scripts to fix IDs or object names.
|
||||
|
||||
## Notes
|
||||
- Run after refactoring/renaming widgets.
|
||||
- Pair with Errors panel for full coverage (`check-errors.md`).
|
||||
|
||||
20
references/comments.md
Normal file
20
references/comments.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Comments in Analytic Applications (2025.23)
|
||||
|
||||
Source: `help-portal-0f47c5728c484f5c86db06b43f11b1db.md` (Work with Comments in Analytic Applications).
|
||||
|
||||
## Supported widgets
|
||||
- Chart, table, geo map, image, shape, text, RSS reader, web page, R visualization, clock.
|
||||
|
||||
## Runtime behavior
|
||||
- Users can view/create comments on widgets or table cells for analytic and planning models.
|
||||
- Comment mode toggle available in toolbar during view mode.
|
||||
- Embedded apps: enable Commenting in Embedded Mode via System Administration → System Configuration.
|
||||
|
||||
## Script APIs (design time)
|
||||
- Show/hide comments programmatically.
|
||||
- Table cell comment APIs: get comments (by ID or data context), post comments, remove comments.
|
||||
|
||||
## Implementation notes
|
||||
- Same table-cell commenting pattern as stories; scripting provides additional control.
|
||||
- Works across analytic and planning models.
|
||||
|
||||
42
references/composites-restrictions.md
Normal file
42
references/composites-restrictions.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Composites Restrictions (2025.23)
|
||||
|
||||
Source: `help-portal-2249cc4e678e4f46ace92501320f5ef6.md` (Restrictions in Composites).
|
||||
|
||||
## File Repository
|
||||
- Cannot schedule publication or publish to catalog.
|
||||
- Not shown in Catalog/Favorites/Shared With Me tabs.
|
||||
|
||||
## Composite Editor – unsupported
|
||||
- View mode, multiple pages, page styling, popups, technical objects, export, bookmark.
|
||||
- Link dimensions/linked models, chart scaling, input task, smart discovery, data analyzer.
|
||||
- Layouts panel, theme, CSS editor, rename account/measure/dimension.
|
||||
- Calculation/account/dimension input controls, save as template, publish to catalog.
|
||||
- Toolbar customization, global settings, copy to, auto refresh, performance tools, loading optimization, filter panel, device preview, commenting.
|
||||
|
||||
## Composite Editor – partial
|
||||
- Widgets: limited set available.
|
||||
- Linked analysis: only “Only This Widget” or “Only Selected Widgets”.
|
||||
- Thresholds: adding to tables not supported.
|
||||
- Edit prompts: auto-open prompt option unavailable.
|
||||
- Dynamic text sources limited; cannot use input controls, story filters, model variables, page number.
|
||||
- Hyperlink: cannot link to specific story page.
|
||||
- Dropdown/checkbox/radio/input: only manual input; write-back unavailable.
|
||||
- Scripting: widget APIs supported; global APIs only partially.
|
||||
- Page styling: ID, title, canvas size, page layout unavailable.
|
||||
- `onInitialization` event: not supported.
|
||||
|
||||
## Optimized Story Experience – unsupported
|
||||
- Linked widgets diagram; video data story.
|
||||
- Copy/paste composites or pages with composites.
|
||||
- Save As in view mode.
|
||||
|
||||
## Optimized Story Experience – partial
|
||||
- Chart color: palette retained only when no dimension/account in Color section.
|
||||
- Export/bookmark: only the composite as a whole, not individual widgets.
|
||||
- Commenting: supported on individual widgets in view mode only.
|
||||
- Linked analysis: composites can only be targets for “All Widgets in Story/Page” or “Only Selected Widgets”.
|
||||
|
||||
## Implementation notes
|
||||
- Scripting support limited to widget APIs; many advanced features absent.
|
||||
- Export works at composite level only.
|
||||
|
||||
18
references/composites-scripting.md
Normal file
18
references/composites-scripting.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Scripting in Composites (Optimized) – 2025.23
|
||||
|
||||
Source: `help-portal-6f7a3c387b7d4efcabcf95990884d4a5.md` (Add Scripts to Your Composites).
|
||||
|
||||
## Add scripts to a composite
|
||||
1. Hover over the composite in the Outline.
|
||||
2. Click the **Edit Scripts** (⚙) icon.
|
||||
3. Select an available event.
|
||||
4. Write scripts for that event.
|
||||
|
||||
## Available APIs
|
||||
- Standard composite APIs for behavior/properties.
|
||||
- Interface functions defined in the composite; value help lists available functions.
|
||||
|
||||
## Implementation notes
|
||||
- Scripts are per-event on the composite.
|
||||
- Use interface functions to coordinate with host story/components.
|
||||
|
||||
640
references/data-actions-tech-object.md
Normal file
640
references/data-actions-tech-object.md
Normal file
@@ -0,0 +1,640 @@
|
||||
# Data Actions Technical Object (2025.23)
|
||||
|
||||
**Source**: [Analytics Designer API Reference - Data Action](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a5/7c52c448e17c4746b6e767b8370f744e.html)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Getting Data Action Reference](#getting-data-action-reference)
|
||||
2. [Execution Methods](#execution-methods)
|
||||
3. [Parameter Management](#parameter-management)
|
||||
4. [Event Handlers](#event-handlers)
|
||||
5. [Execution Status Monitoring](#execution-status-monitoring)
|
||||
6. [Error Handling](#error-handling)
|
||||
7. [Best Practices](#best-practices)
|
||||
|
||||
---
|
||||
|
||||
## Getting Data Action Reference
|
||||
|
||||
Data Actions are widgets that can be accessed directly by their assigned ID in scripts.
|
||||
|
||||
```javascript
|
||||
// Direct access to Data Action widget
|
||||
DataAction_1.execute();
|
||||
```
|
||||
|
||||
**Note**: Data Actions must be added to the story canvas before they can be scripted.
|
||||
|
||||
---
|
||||
|
||||
## Execution Methods
|
||||
|
||||
### execute()
|
||||
|
||||
Executes the data action synchronously.
|
||||
|
||||
```javascript
|
||||
// Simple execution
|
||||
DataAction_1.execute();
|
||||
|
||||
// With parameters (set parameters before execution)
|
||||
DataAction_1.setParameterValue("YEAR", "2024");
|
||||
DataAction_1.setParameterValue("VERSION", "Budget");
|
||||
DataAction_1.execute();
|
||||
```
|
||||
|
||||
### executeInBackground()
|
||||
|
||||
Executes the data action asynchronously without blocking the UI.
|
||||
|
||||
```javascript
|
||||
// Execute in background for long-running actions
|
||||
DataAction_1.executeInBackground();
|
||||
|
||||
// Monitor completion with event handler
|
||||
DataAction_1.onExecutionStatusUpdate = function(status) {
|
||||
if (status === ExecutionStatus.Completed) {
|
||||
Table_1.getDataSource().refreshData();
|
||||
Application.showMessage(ApplicationMessageType.Info, "Data action completed");
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parameter Management
|
||||
|
||||
### setParameterValue(parameterName, value)
|
||||
|
||||
Sets a parameter value for the data action.
|
||||
|
||||
**Parameters**:
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| parameterName | string | Yes | Name of the parameter defined in the data action |
|
||||
| value | string/number | Yes | Value to pass to the parameter |
|
||||
|
||||
```javascript
|
||||
// Set single parameter
|
||||
DataAction_1.setParameterValue("COST_CENTER", "1000");
|
||||
|
||||
// Set multiple parameters
|
||||
var parameters = {
|
||||
"YEAR": "2024",
|
||||
"MONTH": "12",
|
||||
"SCENARIO": "Actual",
|
||||
"AMOUNT": 100000
|
||||
};
|
||||
|
||||
Object.keys(parameters).forEach(function(param) {
|
||||
DataAction_1.setParameterValue(param, parameters[param]);
|
||||
});
|
||||
|
||||
// Execute with all parameters set
|
||||
DataAction_1.execute();
|
||||
```
|
||||
|
||||
### getParameterValue(parameterName)
|
||||
|
||||
Gets the current value of a parameter.
|
||||
|
||||
```javascript
|
||||
var yearValue = DataAction_1.getParameterValue("YEAR");
|
||||
if (yearValue) {
|
||||
console.log("Current year parameter:", yearValue);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Event Handlers
|
||||
|
||||
### onBeforeExecute
|
||||
|
||||
Triggered before the data action executes.
|
||||
|
||||
```javascript
|
||||
DataAction_1.onBeforeExecute = function() {
|
||||
// Show loading indicator
|
||||
Application.showBusyIndicator("Processing data action...");
|
||||
|
||||
// Validate parameters
|
||||
var year = DataAction_1.getParameterValue("YEAR");
|
||||
if (!year) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Year parameter is required"
|
||||
);
|
||||
return false; // Prevent execution
|
||||
}
|
||||
|
||||
// Log execution start
|
||||
console.log("Executing data action at:", new Date());
|
||||
};
|
||||
```
|
||||
|
||||
### onAfterExecute
|
||||
|
||||
Triggered after the data action completes execution.
|
||||
|
||||
```javascript
|
||||
DataAction_1.onAfterExecute = function(result) {
|
||||
// Hide loading indicator
|
||||
Application.hideBusyIndicator();
|
||||
|
||||
// Check execution result
|
||||
if (result.success) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"Data action completed successfully"
|
||||
);
|
||||
|
||||
// Refresh affected data
|
||||
Table_1.getDataSource().refreshData();
|
||||
|
||||
// Log success
|
||||
console.log("Data action executed successfully:", result);
|
||||
} else {
|
||||
// Handle errors
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Data action failed: " + (result.message || "Unknown error")
|
||||
);
|
||||
|
||||
console.error("Data action failed:", result);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### onExecutionStatusUpdate
|
||||
|
||||
Triggered during background execution when status changes.
|
||||
|
||||
```javascript
|
||||
DataAction_1.onExecutionStatusUpdate = function(status) {
|
||||
switch(status) {
|
||||
case ExecutionStatus.Running:
|
||||
console.log("Data action is running...");
|
||||
break;
|
||||
|
||||
case ExecutionStatus.Completed:
|
||||
console.log("Data action completed successfully");
|
||||
Table_1.getDataSource().refreshData();
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"Data action completed"
|
||||
);
|
||||
break;
|
||||
|
||||
case ExecutionStatus.Failed:
|
||||
console.error("Data action failed");
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Data action execution failed"
|
||||
);
|
||||
break;
|
||||
|
||||
case ExecutionStatus.Cancelled:
|
||||
console.log("Data action was cancelled");
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Warning,
|
||||
"Data action cancelled by user"
|
||||
);
|
||||
break;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Execution Status Monitoring
|
||||
|
||||
Monitor the execution state for better user experience.
|
||||
|
||||
```javascript
|
||||
// Function to check data action status
|
||||
function checkDataActionStatus() {
|
||||
var isRunning = DataAction_1.isExecuting();
|
||||
|
||||
if (isRunning) {
|
||||
console.log("Data action is currently executing");
|
||||
return true;
|
||||
} else {
|
||||
console.log("Data action is not executing");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute with status monitoring
|
||||
function executeWithMonitoring() {
|
||||
if (checkDataActionStatus()) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Warning,
|
||||
"Data action is already running"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
DataAction_1.execute();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Try-Catch Pattern
|
||||
|
||||
```javascript
|
||||
function safeExecute() {
|
||||
try {
|
||||
// Validate inputs
|
||||
if (!validateInputs()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute data action
|
||||
DataAction_1.execute();
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error executing data action:", error);
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"An error occurred: " + error.message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function validateInputs() {
|
||||
// Check required parameters
|
||||
var requiredParams = ["YEAR", "VERSION"];
|
||||
var missingParams = [];
|
||||
|
||||
requiredParams.forEach(function(param) {
|
||||
var value = DataAction_1.getParameterValue(param);
|
||||
if (!value) {
|
||||
missingParams.push(param);
|
||||
}
|
||||
});
|
||||
|
||||
if (missingParams.length > 0) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Missing required parameters: " + missingParams.join(", ")
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
### Data Locking Integration
|
||||
|
||||
```javascript
|
||||
// Check data locks before execution
|
||||
function executeWithLockCheck() {
|
||||
var selection = Table_1.getSelections()[0];
|
||||
|
||||
if (selection) {
|
||||
var dataLocking = Table_1.getPlanning().getDataLocking();
|
||||
var lockState = dataLocking.getState(selection);
|
||||
|
||||
if (lockState === DataLockingState.Locked) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Warning,
|
||||
"Data is locked by another user. Cannot execute data action."
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Proceed with execution
|
||||
DataAction_1.execute();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Parameter Validation
|
||||
|
||||
Always validate parameters before execution:
|
||||
|
||||
```javascript
|
||||
function validateAllParameters() {
|
||||
var params = DataAction_1.getParameters();
|
||||
var isValid = true;
|
||||
|
||||
params.forEach(function(param) {
|
||||
var value = DataAction_1.getParameterValue(param.name);
|
||||
|
||||
if (param.isRequired && !value) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Required parameter '" + param.name + "' is missing"
|
||||
);
|
||||
isValid = false;
|
||||
}
|
||||
});
|
||||
|
||||
return isValid;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. User Feedback
|
||||
|
||||
Provide clear feedback to users:
|
||||
|
||||
```javascript
|
||||
function executeWithFeedback() {
|
||||
// Show start message
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"Starting data action execution..."
|
||||
);
|
||||
|
||||
// Set up event handlers
|
||||
DataAction_1.onBeforeExecute = function() {
|
||||
Application.showBusyIndicator("Executing data action...");
|
||||
};
|
||||
|
||||
DataAction_1.onAfterExecute = function(result) {
|
||||
Application.hideBusyIndicator();
|
||||
|
||||
if (result.success) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Success,
|
||||
"Data action completed successfully!"
|
||||
);
|
||||
|
||||
// Refresh data
|
||||
Table_1.getDataSource().refreshData();
|
||||
} else {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Data action failed: " + result.message
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Execute
|
||||
DataAction_1.execute();
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Batch Execution
|
||||
|
||||
Execute multiple data actions efficiently:
|
||||
|
||||
```javascript
|
||||
function executeBatch() {
|
||||
var dataActions = [DataAction_1, DataAction_2, DataAction_3];
|
||||
var completed = 0;
|
||||
var total = dataActions.length;
|
||||
|
||||
Application.showBusyIndicator("Executing batch actions...");
|
||||
|
||||
dataActions.forEach(function(action, index) {
|
||||
action.onAfterExecute = function(result) {
|
||||
completed++;
|
||||
|
||||
if (result.success) {
|
||||
console.log("Action", index + 1, "completed");
|
||||
} else {
|
||||
console.error("Action", index + 1, "failed:", result);
|
||||
}
|
||||
|
||||
// Check if all actions are complete
|
||||
if (completed === total) {
|
||||
Application.hideBusyIndicator();
|
||||
Table_1.getDataSource().refreshData();
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"All data actions completed"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Execute with slight delay to avoid overwhelming system
|
||||
setTimeout(function() {
|
||||
action.execute();
|
||||
}, index * 100);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Performance Considerations
|
||||
|
||||
- Use `executeInBackground()` for long-running actions
|
||||
- Avoid executing multiple actions simultaneously on the same data
|
||||
- Refresh data sources only after all related actions complete
|
||||
- Implement proper error handling to prevent UI freezing
|
||||
|
||||
---
|
||||
|
||||
## Complete Example
|
||||
|
||||
```javascript
|
||||
// Complete example of data action management
|
||||
var DataManager = {
|
||||
// Initialize data action
|
||||
init: function() {
|
||||
// Set up event handlers
|
||||
this.setupEventHandlers();
|
||||
|
||||
// Set default parameters
|
||||
this.setDefaultParameters();
|
||||
},
|
||||
|
||||
// Set up event handlers
|
||||
setupEventHandlers: function() {
|
||||
var self = this;
|
||||
|
||||
DataAction_1.onBeforeExecute = function() {
|
||||
return self.validateBeforeExecute();
|
||||
};
|
||||
|
||||
DataAction_1.onAfterExecute = function(result) {
|
||||
self.handleAfterExecute(result);
|
||||
};
|
||||
|
||||
DataAction_1.onExecutionStatusUpdate = function(status) {
|
||||
self.handleStatusUpdate(status);
|
||||
};
|
||||
},
|
||||
|
||||
// Set default parameters
|
||||
setDefaultParameters: function() {
|
||||
var currentDate = new Date();
|
||||
DataAction_1.setParameterValue("YEAR", currentDate.getFullYear().toString());
|
||||
DataAction_1.setParameterValue("MONTH", (currentDate.getMonth() + 1).toString());
|
||||
},
|
||||
|
||||
// Validate before execution
|
||||
validateBeforeExecute: function() {
|
||||
// Check data locks
|
||||
if (this.checkDataLocks()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate parameters
|
||||
if (!this.validateParameters()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Show loading
|
||||
Application.showBusyIndicator("Executing data action...");
|
||||
return true;
|
||||
},
|
||||
|
||||
// Check data locks
|
||||
checkDataLocks: function() {
|
||||
var selection = Table_1.getSelections()[0];
|
||||
|
||||
if (selection) {
|
||||
var lockState = Table_1.getPlanning().getDataLocking().getState(selection);
|
||||
|
||||
if (lockState === DataLockingState.Locked) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Warning,
|
||||
"Data is locked by another user"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
// Validate parameters
|
||||
validateParameters: function() {
|
||||
var required = ["YEAR", "SCENARIO"];
|
||||
var missing = [];
|
||||
|
||||
required.forEach(function(param) {
|
||||
var value = DataAction_1.getParameterValue(param);
|
||||
if (!value) {
|
||||
missing.push(param);
|
||||
}
|
||||
});
|
||||
|
||||
if (missing.length > 0) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Missing parameters: " + missing.join(", ")
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// Handle after execution
|
||||
handleAfterExecute: function(result) {
|
||||
Application.hideBusyIndicator();
|
||||
|
||||
if (result.success) {
|
||||
this.onSuccess(result);
|
||||
} else {
|
||||
this.onError(result);
|
||||
}
|
||||
},
|
||||
|
||||
// Handle success
|
||||
onSuccess: function(result) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Success,
|
||||
"Data action completed successfully"
|
||||
);
|
||||
|
||||
// Refresh data
|
||||
Table_1.getDataSource().refreshData();
|
||||
|
||||
// Log success
|
||||
console.log("Data action success:", result);
|
||||
},
|
||||
|
||||
// Handle error
|
||||
onError: function(result) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Data action failed: " + (result.message || "Unknown error")
|
||||
);
|
||||
|
||||
console.error("Data action error:", result);
|
||||
},
|
||||
|
||||
// Handle status updates
|
||||
handleStatusUpdate: function(status) {
|
||||
console.log("Data action status:", status);
|
||||
|
||||
switch(status) {
|
||||
case ExecutionStatus.Running:
|
||||
console.log("Execution in progress...");
|
||||
break;
|
||||
case ExecutionStatus.Completed:
|
||||
console.log("Execution completed");
|
||||
break;
|
||||
case ExecutionStatus.Failed:
|
||||
console.error("Execution failed");
|
||||
break;
|
||||
case ExecutionStatus.Cancelled:
|
||||
console.log("Execution cancelled");
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
// Execute data action
|
||||
execute: function(parameters) {
|
||||
// Set parameters if provided
|
||||
if (parameters) {
|
||||
Object.keys(parameters).forEach(function(key) {
|
||||
DataAction_1.setParameterValue(key, parameters[key]);
|
||||
});
|
||||
}
|
||||
|
||||
// Execute
|
||||
DataAction_1.execute();
|
||||
},
|
||||
|
||||
// Execute in background
|
||||
executeInBackground: function(parameters) {
|
||||
// Set parameters if provided
|
||||
if (parameters) {
|
||||
Object.keys(parameters).forEach(function(key) {
|
||||
DataAction_1.setParameterValue(key, parameters[key]);
|
||||
});
|
||||
}
|
||||
|
||||
// Execute in background
|
||||
DataAction_1.executeInBackground();
|
||||
}
|
||||
};
|
||||
|
||||
// Usage example:
|
||||
// Initialize on story load
|
||||
DataManager.init();
|
||||
|
||||
// Execute with custom parameters
|
||||
DataManager.execute({
|
||||
"YEAR": "2024",
|
||||
"SCENARIO": "Budget",
|
||||
"AMOUNT": 100000
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Always handle data locks before executing data actions that modify data
|
||||
- Use `executeInBackground()` for long-running actions to avoid UI freezing
|
||||
- Provide clear user feedback through loading indicators and messages
|
||||
- Validate all required parameters before execution
|
||||
- Consider implementing retry logic for failed executions
|
||||
- Refresh affected data sources after successful data action execution
|
||||
|
||||
13
references/data-change-insights-tech-object.md
Normal file
13
references/data-change-insights-tech-object.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Data Change Insights Technical Object (2025.23)
|
||||
|
||||
Source: `help-portal-1c28c2d941ae44769666b4aee00eacce.md`.
|
||||
|
||||
## Purpose
|
||||
Detect and surface insights from data changes.
|
||||
|
||||
## Capabilities
|
||||
- Track changes and expose them via scripting for user notifications or workflows.
|
||||
|
||||
## Notes
|
||||
- Use with planning scenarios where change awareness is critical.
|
||||
|
||||
19
references/debug-scripts.md
Normal file
19
references/debug-scripts.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Debugging Scripts (2025.23)
|
||||
|
||||
Source: `help-portal-704272da511143d686ca6949040b9a68.md`.
|
||||
|
||||
## Locate scripts in browser devtools
|
||||
- Run the app once, open **Sources** in Chrome DevTools.
|
||||
- Search `Ctrl+P` for files named `<WIDGET_NAME>.<FUNCTION_NAME>.js` inside folder `<APPLICATION_NAME>` under `sandbox.worker.main...`.
|
||||
- `onInitialization` scripts use widget name `Application`.
|
||||
|
||||
## Breakpoints
|
||||
- Add breakpoints on line numbers in DevTools; blue marker indicates active breakpoint.
|
||||
- Multiple breakpoints allowed; click to remove.
|
||||
|
||||
## Debug mode
|
||||
- Add `debugger;` statements in scripts.
|
||||
- Launch app with URL parameter `debug=true` to pause automatically at those statements:
|
||||
`[https://<TENANT>/.../application/<APP_ID>/?mode=present&debug=true`](https://<TENANT>/.../application/<APP_ID>/?mode=present&debug=true`)
|
||||
- Comments are preserved in transformed JS to aid identification.
|
||||
|
||||
425
references/debugging-browser-tools.md
Normal file
425
references/debugging-browser-tools.md
Normal file
@@ -0,0 +1,425 @@
|
||||
# SAC Debugging and Browser Tools Reference
|
||||
|
||||
Complete guide for debugging Analytics Designer scripts using browser developer tools.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Console Logging](#console-logging)
|
||||
2. [Browser Debugging](#browser-debugging)
|
||||
3. [Debug Mode](#debug-mode)
|
||||
4. [Breakpoints](#breakpoints)
|
||||
5. [Script Editor Tools](#script-editor-tools)
|
||||
6. [R Visualization Debugging](#r-visualization-debugging)
|
||||
7. [Performance Logging](#performance-logging)
|
||||
8. [Common Issues](#common-issues)
|
||||
|
||||
---
|
||||
|
||||
## Console Logging
|
||||
|
||||
The primary debugging method using `console.log()`.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```javascript
|
||||
var nth = 1;
|
||||
console.log("Hello World, " + nth.toString());
|
||||
// Output: Hello World, 1
|
||||
```
|
||||
|
||||
### Logging Objects
|
||||
|
||||
```javascript
|
||||
// Log complex objects
|
||||
var selections = Chart_1.getSelections();
|
||||
console.log(selections);
|
||||
|
||||
// Log data source variables
|
||||
var ds = Chart_1.getDataSource();
|
||||
console.log(ds.getVariables());
|
||||
|
||||
// Log member info
|
||||
var members = ds.getMembers("Location", 5);
|
||||
console.log(members);
|
||||
```
|
||||
|
||||
### Viewing Console Output
|
||||
|
||||
1. Run the analytic application
|
||||
2. Press **F12** or **Ctrl+Shift+J** to open Developer Tools
|
||||
3. Go to **Console** tab
|
||||
4. Filter by "Info" type if needed
|
||||
5. Expand `sandbox.worker.main.*.js` entries for your logs
|
||||
|
||||
**Note**: Scripts are stored as minified variables and aren't directly debuggable in the console. Use `console.log()` to inspect values.
|
||||
|
||||
---
|
||||
|
||||
## Browser Debugging
|
||||
|
||||
Detailed steps for debugging Analytics Designer scripts in Chrome browser.
|
||||
|
||||
**Note**: Analytics Designer supports debugging only in **Chrome** browser.
|
||||
|
||||
**Note**: Scripts are transformed before execution, so they won't look exactly like the code in the script editor.
|
||||
|
||||
**Note**: Scripts must run at least once in the current session to appear in dev tools.
|
||||
|
||||
### Script Naming Convention
|
||||
|
||||
Analytics Designer scripts follow a specific naming pattern:
|
||||
|
||||
- **Folder**: `<APPLICATION_NAME>`
|
||||
- **Script**: `<WIDGET_NAME>.<FUNCTION_NAME>.js`
|
||||
|
||||
**Example**:
|
||||
- Application: `My Demo Application`
|
||||
- Widget: `Button_1` with `onClick` event
|
||||
- Script name: `Button_1.onClick.js`
|
||||
- Folder: `My_Demo_Application`
|
||||
|
||||
**Note**: Special characters (except `-` and `.`) are replaced with underscore (`_`).
|
||||
|
||||
### Find Script by Name (Quick Method)
|
||||
|
||||
1. Press **F12** to open Developer Tools
|
||||
2. Press **Ctrl+P**
|
||||
3. Start typing part of the script name (e.g., `Button_1.on`)
|
||||
4. Select from the list
|
||||
|
||||
### Find Script by File Tree
|
||||
|
||||
1. Press **F12** to open Developer Tools
|
||||
2. Select **Sources** tab
|
||||
3. Open node starting with `sandbox.worker.main`
|
||||
4. Open **AnalyticApplication** node
|
||||
5. Find folder with your application name
|
||||
6. Scripts executed in current session appear here
|
||||
|
||||
**File Tree Structure**:
|
||||
```
|
||||
sandbox.worker.main.js
|
||||
└── AnalyticApplication
|
||||
└── My_Demo_Application
|
||||
├── Application.onInitialization.js
|
||||
├── Button_1.onClick.js
|
||||
└── ScriptObject_1.returnText.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debug Mode
|
||||
|
||||
Enable enhanced debugging features with debug mode.
|
||||
|
||||
### Enabling Debug Mode
|
||||
|
||||
Append `;debug=true` to the application URL:
|
||||
|
||||
```
|
||||
[https://your-tenant.sapanalytics.cloud/app.html?story=STORY_ID;debug=true](https://your-tenant.sapanalytics.cloud/app.html?story=STORY_ID;debug=true)
|
||||
```
|
||||
|
||||
### Debug Mode Features
|
||||
|
||||
1. **debugger; statement support**: Pause execution at specific lines
|
||||
2. **Comment preservation**: Comments in scripts are kept in transformed code
|
||||
3. **Script name suffix**: Scripts get `-dbg` suffix (e.g., `Button_1.onClick-dbg.js`)
|
||||
|
||||
### Using debugger; Statement
|
||||
|
||||
With debug mode enabled, add `debugger;` to pause execution:
|
||||
|
||||
```javascript
|
||||
/*
|
||||
* This is a block comment
|
||||
*/
|
||||
|
||||
// Debug point
|
||||
debugger;
|
||||
|
||||
// This is a comment
|
||||
return Text_1.getPlainText();
|
||||
```
|
||||
|
||||
**Advantages over breakpoints**:
|
||||
- Define pause location while writing code
|
||||
- Don't need to find script in dev tools first
|
||||
- Persists across sessions
|
||||
|
||||
---
|
||||
|
||||
## Breakpoints
|
||||
|
||||
Set breakpoints to pause script execution at specific lines.
|
||||
|
||||
### Setting a Breakpoint
|
||||
|
||||
1. Open Developer Tools (F12)
|
||||
2. Navigate to your script in Sources tab
|
||||
3. Click on the **line number** where you want to pause
|
||||
4. A **blue marker** appears on that line
|
||||
|
||||
### Multiple Breakpoints
|
||||
|
||||
Add several breakpoints to pause at different points:
|
||||
- Each clicked line number gets a blue marker
|
||||
- Script pauses at each breakpoint when executed
|
||||
|
||||
### Removing a Breakpoint
|
||||
|
||||
Click on the blue marker to remove it.
|
||||
|
||||
### Execution Controls
|
||||
|
||||
When paused at a breakpoint:
|
||||
|
||||
| Button | Action |
|
||||
|--------|--------|
|
||||
| Resume (F8) | Continue execution |
|
||||
| Step Over (F10) | Execute current line, move to next |
|
||||
| Step Into (F11) | Enter function call |
|
||||
| Step Out (Shift+F11) | Exit current function |
|
||||
|
||||
### Inspecting Variables
|
||||
|
||||
While paused:
|
||||
1. Hover over variables to see values
|
||||
2. Use **Scope** panel to see all variables
|
||||
3. Use **Watch** panel to track specific expressions
|
||||
4. Use **Console** to evaluate expressions
|
||||
|
||||
---
|
||||
|
||||
## Script Editor Tools
|
||||
|
||||
Built-in debugging features in the Analytics Designer script editor.
|
||||
|
||||
### Errors and Warnings
|
||||
|
||||
The Info panel displays validation results:
|
||||
|
||||
1. Open **Info** panel at bottom of designer
|
||||
2. Click **Errors** tab
|
||||
3. Search or filter (errors only, warnings only, both)
|
||||
4. Double-click error to open script and jump to location
|
||||
|
||||
**Visual Indicators**:
|
||||
- **Red underline**: Error
|
||||
- **Orange underline**: Warning
|
||||
- **Red marker**: Error at line number
|
||||
- **Orange marker**: Warning at line number
|
||||
|
||||
### Find References
|
||||
|
||||
Find all places where a widget or scripting object is used:
|
||||
|
||||
1. Right-click object in **Outline**
|
||||
2. Select **Find References**
|
||||
3. Results appear in **Reference list** tab of Info panel
|
||||
|
||||
### Renaming with Refactoring
|
||||
|
||||
When you rename objects, all references update automatically:
|
||||
|
||||
**Renamable Objects**:
|
||||
- Widgets
|
||||
- Gadgets
|
||||
- Script variables
|
||||
- Script objects
|
||||
- Script object functions
|
||||
- Function arguments
|
||||
|
||||
**Renaming Methods**:
|
||||
|
||||
1. **Via Outline**:
|
||||
- Select object in Outline
|
||||
- Click **...** (More) button
|
||||
- Select **Rename**
|
||||
- Enter new name
|
||||
|
||||
2. **Via Styling Panel**:
|
||||
- Select object in Outline
|
||||
- Enter new name in **Name** field
|
||||
- Click **Done** (for script objects)
|
||||
|
||||
3. **Function Arguments**:
|
||||
- Select script object function
|
||||
- Click **Edit** button for argument
|
||||
- Enter new name
|
||||
- Click **Done**
|
||||
|
||||
---
|
||||
|
||||
## R Visualization Debugging
|
||||
|
||||
Debug R scripts and their JavaScript integration.
|
||||
|
||||
### R Widget Runtime Environments
|
||||
|
||||
R widgets have two separate environments:
|
||||
|
||||
1. **R environment**: Server-side, in R engine
|
||||
2. **JavaScript environment**: Browser-side, with other widget scripts
|
||||
|
||||
### Execution Order
|
||||
|
||||
**On Startup**:
|
||||
- R script runs
|
||||
- `onResultChanged` JavaScript event does NOT run (initial state)
|
||||
|
||||
**On Data Change**:
|
||||
1. R script runs first
|
||||
2. `onResultChanged` JavaScript event runs
|
||||
|
||||
### Reading R Environment from JavaScript
|
||||
|
||||
```javascript
|
||||
// R script creates: gmCorrelation <- cor(grossMargin, grossMarginPlan)
|
||||
|
||||
// JavaScript reads the value
|
||||
var nCor = this.getEnvironmentValues().getNumber("gmCorrelation");
|
||||
var sCor = nCor.toString();
|
||||
console.log("Margin Correlation: " + sCor);
|
||||
```
|
||||
|
||||
### Writing to R Environment from JavaScript
|
||||
|
||||
```javascript
|
||||
// Set R environment variable from JavaScript
|
||||
RVisualization_1.getInputParameters().setNumber("userSelection", 0);
|
||||
```
|
||||
|
||||
### Available Methods
|
||||
|
||||
**Reading**:
|
||||
- `getEnvironmentValues().getNumber(variableName)`
|
||||
- `getEnvironmentValues().getString(variableName)`
|
||||
|
||||
**Writing**:
|
||||
- `getInputParameters().setNumber(variableName, value)`
|
||||
- `getInputParameters().setString(variableName, value)`
|
||||
|
||||
---
|
||||
|
||||
## Performance Logging
|
||||
|
||||
Enable detailed performance tracking for optimization.
|
||||
|
||||
### Enabling Performance Logging
|
||||
|
||||
Add URL parameter:
|
||||
```
|
||||
?APP_PERFORMANCE_LOGGING=true
|
||||
```
|
||||
|
||||
### Viewing Performance Data
|
||||
|
||||
In browser console:
|
||||
|
||||
```javascript
|
||||
// Get all performance entries for the application
|
||||
window.sap.raptr.getEntriesByMarker("(Application)")
|
||||
.filter(e => e.entryType === 'measure')
|
||||
.sort((a,b) => (a.startTime + a.duration) - (b.startTime + b.duration));
|
||||
```
|
||||
|
||||
### Performance Entry Properties
|
||||
|
||||
| Property | Description |
|
||||
|----------|-------------|
|
||||
| `name` | Operation name |
|
||||
| `entryType` | Entry type (measure, mark) |
|
||||
| `startTime` | Start timestamp |
|
||||
| `duration` | Execution time in ms |
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Script Not Appearing in Dev Tools
|
||||
|
||||
**Problem**: Can't find script in Sources tab.
|
||||
|
||||
**Solution**: Script must run at least once. Trigger the event (click button, select chart, etc.) then refresh dev tools.
|
||||
|
||||
### Breakpoint Not Hit
|
||||
|
||||
**Problem**: Breakpoint set but execution doesn't pause.
|
||||
|
||||
**Solution**:
|
||||
1. Ensure script is for the correct event
|
||||
2. Check if the event actually triggers
|
||||
3. Try using `debugger;` statement instead
|
||||
|
||||
### debugger; Statement Ignored
|
||||
|
||||
**Problem**: `debugger;` doesn't pause execution.
|
||||
|
||||
**Solution**: Enable debug mode by adding `;debug=true` to URL.
|
||||
|
||||
### Console.log Not Showing
|
||||
|
||||
**Problem**: `console.log()` output not visible.
|
||||
|
||||
**Solution**:
|
||||
1. Open correct browser (Chrome)
|
||||
2. Check Console tab
|
||||
3. Clear filters that might hide output
|
||||
4. Look in `sandbox.worker.main.*.js` entries
|
||||
|
||||
### Variables Show as undefined
|
||||
|
||||
**Problem**: Variables appear undefined when inspected.
|
||||
|
||||
**Solution**: Scripts are transformed before execution. Variable names may differ. Use `console.log()` to inspect actual values.
|
||||
|
||||
### Script Looks Different in Debugger
|
||||
|
||||
**Problem**: Code in debugger doesn't match script editor.
|
||||
|
||||
**This is expected**: Analytics Designer transforms scripts before browser execution. The logic is the same, but syntax may differ.
|
||||
|
||||
---
|
||||
|
||||
## Debugging Checklist
|
||||
|
||||
Before debugging:
|
||||
|
||||
- [ ] Using Chrome browser
|
||||
- [ ] Developer Tools open (F12)
|
||||
- [ ] Script has been executed at least once
|
||||
- [ ] For `debugger;`, debug mode enabled (`;debug=true`)
|
||||
- [ ] Console tab open for `console.log()` output
|
||||
- [ ] Sources tab open for breakpoints
|
||||
|
||||
During debugging:
|
||||
|
||||
- [ ] Start with simple `console.log()` to verify code runs
|
||||
- [ ] Check Info panel for script errors first
|
||||
- [ ] Use Find References to understand dependencies
|
||||
- [ ] Test incrementally (every 5-10 lines)
|
||||
- [ ] Check variable types match expected
|
||||
|
||||
---
|
||||
|
||||
## Keyboard Shortcuts Summary
|
||||
|
||||
| Shortcut | Action |
|
||||
|----------|--------|
|
||||
| F12 | Open Developer Tools |
|
||||
| Ctrl+Shift+J | Open Console directly |
|
||||
| Ctrl+P | Quick file search (in Sources) |
|
||||
| F8 | Resume execution |
|
||||
| F10 | Step over |
|
||||
| F11 | Step into |
|
||||
| Shift+F11 | Step out |
|
||||
|
||||
For script editor shortcuts, see: [https://help.sap.com/doc/00f68c2e08b941f081002fd3691d86a7/release/en-US/68dfa2fd057c4d13ad2772825e83b491.html](https://help.sap.com/doc/00f68c2e08b941f081002fd3691d86a7/release/en-US/68dfa2fd057c4d13ad2772825e83b491.html)
|
||||
|
||||
---
|
||||
|
||||
**Source**: SAP Analytics Designer Development Guide - Chapter 4: Scripting in Analytics Designer
|
||||
**Last Updated**: 2025-11-23
|
||||
33
references/explorer-smart-insights.md
Normal file
33
references/explorer-smart-insights.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Explorer & Smart Insights (2025.23)
|
||||
|
||||
Source: `help-portal-a7bd9775a97748d685464b2ef58bce21.md` (Work with Explorer and Smart Insights).
|
||||
|
||||
## Launch Explorer
|
||||
- Manual: Save/run app → select chart/table → More Actions → Open Explorer.
|
||||
- Script API: launch via script; can add extra dimensions/measures to exploration scope.
|
||||
|
||||
## Explorer features
|
||||
- Change dimensions/measures, chart types; show/hide elements.
|
||||
- Table sorting; export data to file.
|
||||
- “Open in New Story” (enable in widget styling quick menus).
|
||||
|
||||
## Apply explorer results
|
||||
1. Create **Data Explorer Configuration** technical object (Outline → Scripting).
|
||||
2. Add custom menu items in Menu Settings.
|
||||
3. Implement `onMenuItemSelect()` script.
|
||||
4. Users pick custom menu items in explorer visualization area.
|
||||
|
||||
## Saving results
|
||||
- Bookmark the application.
|
||||
- Publish to PDF.
|
||||
|
||||
## Smart Insights (requires Explorer enabled)
|
||||
1. Select a data point (chart) or cell (table).
|
||||
2. Choose **Smart Insights** in context menu.
|
||||
3. Insights include correlations, exceptions, clusters, links, predictions.
|
||||
4. Continue exploration or create story (if Open in New Story enabled).
|
||||
|
||||
## Implementation notes
|
||||
- Smart Insights depends on Explorer being enabled.
|
||||
- Custom menu items let users apply exploration results to other widgets.
|
||||
|
||||
14
references/export-pdf-tech-object.md
Normal file
14
references/export-pdf-tech-object.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Export to PDF Technical Object (2025.23)
|
||||
|
||||
Source: `help-portal-c9ab6a29d2bc4182be740685cf7ebb20.md`.
|
||||
|
||||
## Purpose
|
||||
Trigger PDF export programmatically for analytic applications.
|
||||
|
||||
## Capabilities
|
||||
- Configure export scope and options via API.
|
||||
- Integrate export into custom UI (buttons, workflows).
|
||||
|
||||
## Notes
|
||||
- Consider export limitations (see `analytics-designer-restrictions.md`).
|
||||
|
||||
13
references/export-ppt-tech-object.md
Normal file
13
references/export-ppt-tech-object.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Export to PowerPoint Technical Object (2025.23)
|
||||
|
||||
Source: `help-portal-1f75c4965591451c87e7cf89a4c50eeb.md`.
|
||||
|
||||
## Purpose
|
||||
Programmatic export of application content to PowerPoint.
|
||||
|
||||
## Capabilities
|
||||
- Configure export settings and trigger via scripts.
|
||||
|
||||
## Notes
|
||||
- Align slide output expectations with widget visibility and layouts.
|
||||
|
||||
26
references/geomap.md
Normal file
26
references/geomap.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Geo Map Widget (2025.23)
|
||||
|
||||
Source: `help-portal-7200e99b1cdc4463bf5f60ab3d2ab8ae.md` (Geo Map).
|
||||
|
||||
## Properties
|
||||
- **Name**: unique string, default `GeoMap_1`.
|
||||
|
||||
## Quick menu properties (defaults)
|
||||
- Drill (Enabled) – filter selected data points.
|
||||
- Full Screen (Enabled).
|
||||
- Create Story from Widget (Disabled).
|
||||
- Filter/Exclude (Enabled).
|
||||
|
||||
### Visibility control
|
||||
- Main toggle “Visible in Runtime” plus individual checkboxes for each quick menu item.
|
||||
|
||||
## Scripting support
|
||||
- Change layers.
|
||||
- Manipulate properties.
|
||||
- Handle geo map events.
|
||||
|
||||
## Implementation notes
|
||||
- Works in stories and analytic applications.
|
||||
- Quick menu items can be controlled individually or globally.
|
||||
- See Analytics Designer API Reference for detailed APIs.
|
||||
|
||||
314
references/iframe-embedding-lumira-migration.md
Normal file
314
references/iframe-embedding-lumira-migration.md
Normal file
@@ -0,0 +1,314 @@
|
||||
# iFrame Embedding and Lumira Designer Migration
|
||||
|
||||
Reference documentation for iFrame PostMessage communication and migration guidance from Lumira Designer to SAP Analytics Cloud.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [iFrame Embedding with PostMessage](#iframe-embedding-with-postmessage)
|
||||
2. [Differences Between SAP Analytics Cloud and Lumira Designer](#differences-between-sap-analytics-cloud-and-lumira-designer)
|
||||
|
||||
---
|
||||
|
||||
## iFrame Embedding with PostMessage
|
||||
|
||||
When your analytic application is embedded in an iFrame, it can communicate bidirectionally with the host web application using JavaScript PostMessage.
|
||||
|
||||
### Application Events for Embedded Scenarios
|
||||
|
||||
SAC analytic applications have two primary application events:
|
||||
|
||||
1. **onInitialization**: Runs once when the application is instantiated by a user
|
||||
2. **onPostMessageReceived**: Triggered when the host application sends a PostMessage
|
||||
|
||||
### onPostMessageReceived Event
|
||||
|
||||
This event fires whenever the host application makes a PostMessage call into the embedded analytic application.
|
||||
|
||||
**Reference**: [https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
|
||||
|
||||
### Event Parameters
|
||||
|
||||
The `onPostMessageReceived` event provides two input parameters:
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `origin` | string | The domain of the host application |
|
||||
| `message` | any | The message content passed via PostMessage |
|
||||
|
||||
### origin Parameter
|
||||
|
||||
The `origin` parameter contains the domain of the host application.
|
||||
|
||||
**Important Security Notes**:
|
||||
- iFrame contents don't need to be in the same origin as the host app
|
||||
- Same-origin policies may still be in effect
|
||||
- Be careful about **clickjacking attacks** and **malicious iFrame hosts**
|
||||
- **Always validate** the `origin` parameter to ensure the iFrame host is expected
|
||||
|
||||
**Security Best Practice**:
|
||||
```javascript
|
||||
// onPostMessageReceived event
|
||||
if (origin !== "[https://trusted-domain.com](https://trusted-domain.com)") {
|
||||
console.log("Unauthorized origin: " + origin);
|
||||
return; // Reject messages from unknown origins
|
||||
}
|
||||
|
||||
// Process message from trusted origin
|
||||
processMessage(message);
|
||||
```
|
||||
|
||||
### message Parameter
|
||||
|
||||
The `message` parameter contains the standard JavaScript PostMessage content passed into SAP Analytics Cloud.
|
||||
|
||||
**Characteristics**:
|
||||
- Does **not** follow any specific format
|
||||
- Could contain almost any data type
|
||||
- Encoded using the **structured clone algorithm**
|
||||
- Some documented restrictions on what can and can't be encoded
|
||||
|
||||
### Structured Clone Algorithm
|
||||
|
||||
The structured clone algorithm has some limitations on what can be cloned:
|
||||
|
||||
**Supported**:
|
||||
- Primitive types (string, number, boolean)
|
||||
- Arrays
|
||||
- Plain objects
|
||||
- Date objects
|
||||
- Map, Set, ArrayBuffer, TypedArray
|
||||
|
||||
**Not Supported**:
|
||||
- Functions
|
||||
- DOM nodes
|
||||
- Symbols
|
||||
- Error objects
|
||||
- Property descriptors, setters, getters
|
||||
|
||||
### Example: Receiving PostMessage
|
||||
|
||||
```javascript
|
||||
// onPostMessageReceived event handler
|
||||
// Parameters: origin, message
|
||||
|
||||
// Security check
|
||||
if (origin !== "[https://my-portal.company.com](https://my-portal.company.com)") {
|
||||
console.log("Rejected message from: " + origin);
|
||||
return;
|
||||
}
|
||||
|
||||
// Log the received message
|
||||
console.log("Received message from: " + origin);
|
||||
console.log("Message content: " + JSON.stringify(message));
|
||||
|
||||
// Process message based on type
|
||||
if (message.type === "filter") {
|
||||
// Apply filter from host application
|
||||
var dimension = message.dimension;
|
||||
var value = message.value;
|
||||
|
||||
Chart_1.getDataSource().setDimensionFilter(dimension, value);
|
||||
|
||||
} else if (message.type === "refresh") {
|
||||
// Refresh data
|
||||
Chart_1.getDataSource().refreshData();
|
||||
|
||||
} else if (message.type === "navigate") {
|
||||
// Handle navigation request
|
||||
var pageId = message.pageId;
|
||||
// Navigate to page...
|
||||
}
|
||||
```
|
||||
|
||||
### Host Application Example
|
||||
|
||||
In the host web application (outside SAP Analytics Cloud):
|
||||
|
||||
```javascript
|
||||
// Get reference to the SAC iFrame
|
||||
var sacFrame = document.getElementById("sac-iframe");
|
||||
|
||||
// Send filter command to embedded SAC application
|
||||
sacFrame.contentWindow.postMessage({
|
||||
type: "filter",
|
||||
dimension: "Region",
|
||||
value: "EMEA"
|
||||
}, "[https://your-tenant.sapanalytics.cloud](https://your-tenant.sapanalytics.cloud)");
|
||||
|
||||
// Send refresh command
|
||||
sacFrame.contentWindow.postMessage({
|
||||
type: "refresh"
|
||||
}, "[https://your-tenant.sapanalytics.cloud](https://your-tenant.sapanalytics.cloud)");
|
||||
```
|
||||
|
||||
### Security Recommendations
|
||||
|
||||
1. **Always validate origin**: Check that messages come from expected domains
|
||||
2. **Use specific target origins**: Don't use `"*"` as target origin
|
||||
3. **Validate message structure**: Ensure message format matches expectations
|
||||
4. **Log suspicious activity**: Track rejected messages for security monitoring
|
||||
5. **Implement allowlist**: Maintain list of trusted origins
|
||||
|
||||
```javascript
|
||||
// Recommended: Origin allowlist pattern
|
||||
var trustedOrigins = [
|
||||
"[https://portal.company.com",](https://portal.company.com",)
|
||||
"[https://intranet.company.com"](https://intranet.company.com")
|
||||
];
|
||||
|
||||
// In onPostMessageReceived
|
||||
var isTrusted = false;
|
||||
for (var i = 0; i < trustedOrigins.length; i++) {
|
||||
if (origin === trustedOrigins[i]) {
|
||||
isTrusted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isTrusted) {
|
||||
console.log("Blocked message from untrusted origin: " + origin);
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Differences Between SAP Analytics Cloud and Lumira Designer
|
||||
|
||||
Design Studio / Lumira Designer and SAP Analytics Cloud, analytics designer have broadly similar scripting environments. Both are JavaScript-based and perform similar missions. Analytics designer's scripting framework was informed by experience with Design Studio.
|
||||
|
||||
However, there are important differences to understand when migrating.
|
||||
|
||||
### Execution Environment
|
||||
|
||||
| Aspect | Lumira Designer | Analytics Designer (SAC) |
|
||||
|--------|----------------|--------------------------|
|
||||
| **Script Execution** | Server-side | Browser-side (client) |
|
||||
| **Proximity** | Close to the data | Close to the user |
|
||||
| **JavaScript Engine** | Server JavaScript engine | Browser native JavaScript |
|
||||
|
||||
**Key Implication**: This "close-to-data vs close-to-user" philosophical difference affects how you design scripts.
|
||||
|
||||
### Copy-and-Paste Compatibility
|
||||
|
||||
**Analytics Designer is NOT copy-and-paste compatible with Lumira Designer.**
|
||||
|
||||
Scripts written for Lumira Designer will likely need modification to work in Analytics Designer. This is partially a consequence of the architectural differences.
|
||||
|
||||
### Data Source Access
|
||||
|
||||
| Feature | Lumira Designer | Analytics Designer |
|
||||
|---------|----------------|-------------------|
|
||||
| Standalone Data Sources | Available as global variables | Hidden within data-bound widgets |
|
||||
| Access Method | Direct access by name | Must use `getDataSource()` method |
|
||||
|
||||
**Lumira Designer**:
|
||||
```javascript
|
||||
// Direct access to data source
|
||||
DS_1.setFilter("Location", "USA");
|
||||
```
|
||||
|
||||
**Analytics Designer**:
|
||||
```javascript
|
||||
// Access through widget
|
||||
var ds = Chart_1.getDataSource();
|
||||
ds.setDimensionFilter("Location", "USA");
|
||||
```
|
||||
|
||||
**Note**: When standalone data sources become available in SAC, you'll be able to access them as global variables, similar to Lumira.
|
||||
|
||||
### Type System Differences
|
||||
|
||||
| Feature | Lumira Designer | Analytics Designer |
|
||||
|---------|----------------|-------------------|
|
||||
| Type Conversion | More lenient | Strict (no automatic conversion) |
|
||||
| Equality Operator | `==` works across types | `==` only valid with same types |
|
||||
| Recommended Operator | `==` or `===` | `===` (strict equality) |
|
||||
|
||||
**Lumira Designer** (allowed):
|
||||
```javascript
|
||||
var aNumber = 1;
|
||||
if (aNumber == "1") { // Works - auto type coercion
|
||||
// Executes
|
||||
}
|
||||
```
|
||||
|
||||
**Analytics Designer** (must use explicit conversion or strict equality):
|
||||
```javascript
|
||||
var aNumber = 1;
|
||||
|
||||
// Option 1: Strict equality with correct type
|
||||
if (aNumber === 1) { // Recommended
|
||||
// Executes
|
||||
}
|
||||
|
||||
// Option 2: Explicit conversion
|
||||
if (aNumber.toString() === "1") {
|
||||
// Executes
|
||||
}
|
||||
|
||||
// WRONG: Different types with ==
|
||||
// if (aNumber == "1") { } // Error in SAC!
|
||||
```
|
||||
|
||||
### API Method Names
|
||||
|
||||
Some API methods have different names between platforms:
|
||||
|
||||
| Operation | Lumira Designer | Analytics Designer |
|
||||
|-----------|----------------|-------------------|
|
||||
| Set filter | `setFilter()` | `setDimensionFilter()` |
|
||||
| Remove filter | `removeFilter()` | `removeDimensionFilter()` |
|
||||
| Get filter | `getFilter()` | `getDimensionFilters()` |
|
||||
|
||||
### Summary of Key Migration Steps
|
||||
|
||||
When migrating from Lumira Designer to SAP Analytics Cloud:
|
||||
|
||||
1. **Update data source access**: Change direct data source references to `widget.getDataSource()`
|
||||
|
||||
2. **Fix type conversions**: Add explicit `.toString()` or other conversion methods
|
||||
|
||||
3. **Update equality operators**: Change `==` to `===` for comparisons, especially with different types
|
||||
|
||||
4. **Update API method names**: Replace Lumira-specific methods with SAC equivalents
|
||||
|
||||
5. **Review script location**: Consider that scripts now run in browser, not on server
|
||||
|
||||
6. **Test thoroughly**: Behavior may differ due to execution environment changes
|
||||
|
||||
### Example Migration
|
||||
|
||||
**Original Lumira Designer Script**:
|
||||
```javascript
|
||||
// Lumira Designer
|
||||
DS_1.setFilter("Year", selectedYear);
|
||||
var members = DS_1.getMembers("Location");
|
||||
var count = members.length;
|
||||
console.log("Count: " + count); // Auto-converts count to string
|
||||
```
|
||||
|
||||
**Migrated Analytics Designer Script**:
|
||||
```javascript
|
||||
// Analytics Designer (SAC)
|
||||
var ds = Chart_1.getDataSource();
|
||||
ds.setDimensionFilter("Year", selectedYear);
|
||||
var members = ds.getMembers("Location");
|
||||
var count = members.length;
|
||||
console.log("Count: " + count.toString()); // Explicit conversion required
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- **SAP Analytics Designer Documentation**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD)
|
||||
- **Lumira Designer Migration Guide**: Check SAP Help Portal for official migration documentation
|
||||
- **PostMessage API**: [https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
|
||||
|
||||
---
|
||||
|
||||
**Source**: SAP Analytics Designer Development Guide - Chapter 4: Scripting in Analytics Designer (Sections 4.2.3.1 and 4.10)
|
||||
**Last Updated**: 2025-11-23
|
||||
15
references/input-fields.md
Normal file
15
references/input-fields.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Input Fields & Text Areas (2025.23)
|
||||
|
||||
Source: `help-portal-c82546f4ec8546698b69b426c08d1450.md`.
|
||||
|
||||
## Overview
|
||||
Guidance for scripting input fields and text areas in analytic applications.
|
||||
|
||||
## Key points
|
||||
- Configure input controls and text areas for user entry.
|
||||
- Validate and bind entered values to variables or data sources.
|
||||
- Handle events for change/submit to trigger downstream logic.
|
||||
|
||||
## Notes
|
||||
- Use appropriate data types; sanitize input before applying to filters or actions.
|
||||
|
||||
11
references/memberinfo-performance.md
Normal file
11
references/memberinfo-performance.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# MemberInfo Performance Tip (2025.23)
|
||||
|
||||
Source: `help-portal-e821badae672480193d58608128f5bf7.md`.
|
||||
|
||||
## Purpose
|
||||
Guidance to improve performance when using `MemberInfo` objects.
|
||||
|
||||
## Notes
|
||||
- Use efficient access patterns to avoid unnecessary backend calls.
|
||||
- Cache member info where possible when used repeatedly.
|
||||
|
||||
14
references/multi-actions-tech-object.md
Normal file
14
references/multi-actions-tech-object.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Multi Actions Technical Object (2025.23)
|
||||
|
||||
Source: `help-portal-3bbc80d4df134f2887a037a85a4ea700.md`.
|
||||
|
||||
## Purpose
|
||||
Run multiple actions in sequence/parallel as a packaged operation.
|
||||
|
||||
## Capabilities
|
||||
- Configure and execute multi actions from scripts.
|
||||
- Combine data actions and other steps for end-to-end automation.
|
||||
|
||||
## Notes
|
||||
- Monitor execution status; provide user feedback during long runs.
|
||||
|
||||
14
references/odata-service.md
Normal file
14
references/odata-service.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# OData Service (2025.23)
|
||||
|
||||
Source: `help-portal-05a5e8a72280470e9e6c174c35bc993a.md` (and duplicate -2).
|
||||
|
||||
## Purpose
|
||||
OData service technical object for integrating external data via OData in analytic applications.
|
||||
|
||||
## Capabilities
|
||||
- Configure service endpoint and consume OData feeds.
|
||||
- Use in scripts to read/manipulate OData-sourced data.
|
||||
|
||||
## Notes
|
||||
- Duplicate extract exists (`...-2.md`); content identical.
|
||||
|
||||
14
references/on-after-data-entry-process.md
Normal file
14
references/on-after-data-entry-process.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Table onAfterDataEntryProcess Event (2025.23)
|
||||
|
||||
Source: `help-portal-6d801f5209df484fbfef6a4a90ce53ff.md`.
|
||||
|
||||
## Purpose
|
||||
Event hook fired after data entry processing in tables.
|
||||
|
||||
## Usage
|
||||
- Attach scripts to react after user data entry is processed (for example, validations, refreshes, follow-up actions).
|
||||
- Suitable for planning tables to confirm writebacks or update dependent widgets.
|
||||
|
||||
## Notes
|
||||
- Keep handlers lightweight to avoid post-entry lag.
|
||||
|
||||
14
references/optimize-type-libraries.md
Normal file
14
references/optimize-type-libraries.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Optimize Type Libraries (2025.23)
|
||||
|
||||
Source: `help-portal-919e7adeed7744ffb96539fce4f01945.md`.
|
||||
|
||||
## Purpose
|
||||
Improve script editor performance and code completion by optimizing loaded type libraries.
|
||||
|
||||
## Guidance
|
||||
- Prune unused libraries to speed up value help and reduce noise.
|
||||
- Keep frequently used objects/types loaded for productivity.
|
||||
|
||||
## Implementation notes
|
||||
- Use Help Portal guidance to manage type library settings within Analytics Designer.
|
||||
|
||||
15
references/pattern-functions.md
Normal file
15
references/pattern-functions.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Pattern-Based Functions (2025.23)
|
||||
|
||||
Source: `help-portal-9af108a1175845b2be00d49c92bd81ad.md`.
|
||||
|
||||
## Overview
|
||||
Pattern-based functions leverage ML to recognize text patterns and return structured outputs.
|
||||
|
||||
## Usage examples
|
||||
- Detect email, URL, phone patterns in text fields.
|
||||
- Validate or extract pattern matches for downstream logic.
|
||||
|
||||
## Notes
|
||||
- Use for user input validation and transformation.
|
||||
- Combine with error messaging when patterns are not matched.
|
||||
|
||||
14
references/planning-model-tech-object.md
Normal file
14
references/planning-model-tech-object.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Planning Model Technical Object (2025.23)
|
||||
|
||||
Source: `help-portal-edb6447bac6346b3a4908ffd5956b796.md`.
|
||||
|
||||
## Purpose
|
||||
Expose planning model interactions via a technical object for scripts.
|
||||
|
||||
## Capabilities
|
||||
- Access model metadata and operations in analytic applications.
|
||||
- Coordinate with planning APIs for data entry, locking, and version handling.
|
||||
|
||||
## Notes
|
||||
- Use alongside table planning APIs for end-to-end planning workflows.
|
||||
|
||||
21
references/script-editor.md
Normal file
21
references/script-editor.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Script Editor Basics (2025.23)
|
||||
|
||||
Source: `help-portal-2560f7cb6c2745dab84d2e12652a12b4.md` (Write Scripts in Script Editor).
|
||||
|
||||
## Purpose
|
||||
Write JavaScript-based scripts on widgets/pages/popups to drive interactivity in analytic applications or optimized stories.
|
||||
|
||||
## Open the editor
|
||||
1. In Outline, hover widget/page/popup → **Edit Scripts** (gear).
|
||||
2. If multiple events exist, choose the event (for example, `onSelect`, `onInitialization`).
|
||||
3. Editor opens in new tab titled `<Widget> - <Event>`.
|
||||
|
||||
## Authoring pattern
|
||||
`<ComponentVariable>.<function>(<arguments>);`
|
||||
|
||||
## Notes
|
||||
- View available objects/functions in Analytics Designer or Optimized Story Experience API references.
|
||||
- Edit Scripts also available via widget **More Actions** menu.
|
||||
- Related indicator (gear) shows when scripts exist; run in view mode to test.
|
||||
- See also: script variables (`script-variables.md`), script objects (`script-objects.md`), keyboard shortcuts (Help Portal).
|
||||
|
||||
17
references/script-objects.md
Normal file
17
references/script-objects.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Script Objects (2025.23)
|
||||
|
||||
Source: `help-portal-c3993b39e8564fe3aad3dadae90f2e7e.md`.
|
||||
|
||||
## Purpose
|
||||
Reusable functions decoupled from widget events; callable across the application.
|
||||
|
||||
## Create
|
||||
1. Outline → Script Objects → (+)
|
||||
2. Define functions inside the object file.
|
||||
3. Call from events: `MyScriptObject.myFunction(args);`
|
||||
|
||||
## Best practices
|
||||
- Group related helpers per domain (filters, formatting, navigation).
|
||||
- Avoid UI-specific IDs inside script objects; pass them as parameters.
|
||||
- Keep functions small; document expected inputs/outputs.
|
||||
|
||||
15
references/script-performance-popup.md
Normal file
15
references/script-performance-popup.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Script Performance Popup (2025.23)
|
||||
|
||||
Source: `help-portal-39b680bd2d914f0cbeb81638df3c9084.md`.
|
||||
|
||||
## Purpose
|
||||
Analyze script execution performance in analytic applications.
|
||||
|
||||
## Usage
|
||||
- Open the performance popup to inspect execution time and bottlenecks of recent script runs.
|
||||
- Use after complex interactions to spot slow functions.
|
||||
|
||||
## Notes
|
||||
- Combine with batching/pause-refresh patterns to improve slow sections.
|
||||
- Re-run scenarios after optimization to compare results.
|
||||
|
||||
20
references/script-variables.md
Normal file
20
references/script-variables.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Script Variables (2025.23)
|
||||
|
||||
Source: `help-portal-82bbae6b7005482c87bfd59a91735b45.md`.
|
||||
|
||||
## Types & scope
|
||||
- Global variables defined in Outline → Global Variables (String, Integer, Number, Boolean; arrays allowed).
|
||||
- Runtime scope across application; accessible from any script.
|
||||
- URL parameter support: prefix variable with `p_` to read from query string.
|
||||
|
||||
## Usage
|
||||
- Read/write directly: `MyGlobalVar = "value";`
|
||||
- Use as dynamic text sources (see `text-widget.md`).
|
||||
- Use in filters, bindings, and calculations.
|
||||
|
||||
## URL parameter pattern
|
||||
- `...?p_MyVar=North` sets global variable `MyVar` at startup.
|
||||
|
||||
## Notes
|
||||
- Keep naming consistent; document variable intent in Outline description.
|
||||
|
||||
509
references/scripting-language-fundamentals.md
Normal file
509
references/scripting-language-fundamentals.md
Normal file
@@ -0,0 +1,509 @@
|
||||
# SAC Scripting Language Fundamentals
|
||||
|
||||
Complete reference for the Analytics Designer scripting language based on official SAP documentation.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Language Overview](#language-overview)
|
||||
2. [Type System](#type-system)
|
||||
3. [Variables and Scope](#variables-and-scope)
|
||||
4. [Control Flow](#control-flow)
|
||||
5. [Loops](#loops)
|
||||
6. [Operators](#operators)
|
||||
7. [Built-in Objects](#built-in-objects)
|
||||
8. [Arrays](#arrays)
|
||||
9. [Method Chaining](#method-chaining)
|
||||
10. [Script Runtime Security](#script-runtime-security)
|
||||
|
||||
---
|
||||
|
||||
## Language Overview
|
||||
|
||||
The Analytics Designer scripting language is a **limited subset of JavaScript** with the following characteristics:
|
||||
|
||||
- Extended with a logical type system enforcing **type safety at design time**
|
||||
- Executes natively in the browser as true JavaScript
|
||||
- All scripts run and validate against **strict mode**
|
||||
- Some advanced JavaScript features are hidden
|
||||
- Scripts are tied to **events** or **global script objects**
|
||||
|
||||
### What Makes It Different from Standard JavaScript
|
||||
|
||||
| Feature | Standard JavaScript | Analytics Designer |
|
||||
|---------|--------------------|--------------------|
|
||||
| Typing | Weak, dynamic | Strong, static |
|
||||
| Type conversion | Automatic | Must be explicit |
|
||||
| Variable reuse | Can change type | Type is fixed |
|
||||
| External libraries | Can import | Not supported |
|
||||
| DOM access | Full access | Blocked |
|
||||
| Global variables | Accessible | Isolated |
|
||||
|
||||
---
|
||||
|
||||
## Type System
|
||||
|
||||
### Strong and Static Typing
|
||||
|
||||
Analytics Designer enforces strict types to enable powerful tooling (code completion, validation).
|
||||
|
||||
**Key Rules**:
|
||||
1. Once a variable has a type, it **cannot change**
|
||||
2. Variable names **cannot be reused** for different types
|
||||
3. Type conversions must be **explicit**
|
||||
|
||||
### No Automatic Type Casting
|
||||
|
||||
Standard JavaScript allows implicit type coercion:
|
||||
|
||||
```javascript
|
||||
// Valid JavaScript, but ERROR in Analytics Designer
|
||||
var nth = 1;
|
||||
console.log("Hello World, " + nth); // Auto-converts nth to string
|
||||
```
|
||||
|
||||
In Analytics Designer, you must explicitly cast:
|
||||
|
||||
```javascript
|
||||
// Correct: Explicit type conversion
|
||||
var nth = 1;
|
||||
console.log("Hello World, " + nth.toString());
|
||||
```
|
||||
|
||||
### Type Declaration
|
||||
|
||||
Variables are declared with `var` and their type is inferred from the initial value:
|
||||
|
||||
```javascript
|
||||
var myString = "Hello"; // Type: string
|
||||
var myNumber = 42; // Type: integer
|
||||
var myDecimal = 3.14; // Type: number
|
||||
var myBoolean = true; // Type: boolean
|
||||
var myArray = ["a", "b"]; // Type: string[]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Variables and Scope
|
||||
|
||||
### Local Variables
|
||||
|
||||
Declared within event handlers or functions, scoped to that block:
|
||||
|
||||
```javascript
|
||||
// In onSelect event
|
||||
var selectedValue = Chart_1.getSelections()[0];
|
||||
var filterValue = selectedValue["Location"];
|
||||
```
|
||||
|
||||
### Global Variables
|
||||
|
||||
Created via Outline panel → Script Variables → (+). Available across entire application:
|
||||
|
||||
```javascript
|
||||
// Access global variable from any script
|
||||
GlobalVariable_1 = "new value";
|
||||
var currentValue = GlobalVariable_1;
|
||||
```
|
||||
|
||||
### Script Variables in Calculated Measures
|
||||
|
||||
Script variables can be referenced in calculated measures for what-if simulations:
|
||||
|
||||
```javascript
|
||||
// Formula: [Gross_Margin] * [@ScriptVariable_Rate]
|
||||
// Change ScriptVariable_Rate via script to see updated results
|
||||
```
|
||||
|
||||
### URL Parameters
|
||||
|
||||
Prefix global variable name with `p_` to pass values via URL:
|
||||
|
||||
```
|
||||
[https://your-sac-tenant.com/app?p_myVariable=value](https://your-sac-tenant.com/app?p_myVariable=value)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Control Flow
|
||||
|
||||
### if/else Statements
|
||||
|
||||
Standard syntax, but remember type safety rules:
|
||||
|
||||
```javascript
|
||||
if (nth === 1) {
|
||||
console.log("if...");
|
||||
} else if (nth < 3) {
|
||||
console.log("else if...");
|
||||
} else {
|
||||
console.log("else...");
|
||||
}
|
||||
```
|
||||
|
||||
### switch Statements
|
||||
|
||||
Standard JavaScript switch is supported:
|
||||
|
||||
```javascript
|
||||
switch (i) {
|
||||
case 0:
|
||||
day = "Zero";
|
||||
break;
|
||||
case 1:
|
||||
day = "One";
|
||||
break;
|
||||
case 2:
|
||||
day = "Two";
|
||||
break;
|
||||
default:
|
||||
day = "Unknown";
|
||||
}
|
||||
```
|
||||
|
||||
### break Statement
|
||||
|
||||
Use `break` to exit loops and switch statements.
|
||||
|
||||
---
|
||||
|
||||
## Loops
|
||||
|
||||
### for Loop
|
||||
|
||||
**Important**: You must explicitly declare the loop iterator.
|
||||
|
||||
```javascript
|
||||
// ERROR: Iterator not declared
|
||||
for (i = 0; i < 3; i++) {
|
||||
console.log(i.toString());
|
||||
}
|
||||
|
||||
// CORRECT: Iterator declared with var
|
||||
for (var i = 0; i < 3; i++) {
|
||||
console.log(i.toString());
|
||||
}
|
||||
```
|
||||
|
||||
### while Loop
|
||||
|
||||
Fully supported:
|
||||
|
||||
```javascript
|
||||
var nth = 1;
|
||||
while (nth < 3) {
|
||||
console.log("Hello while, " + nth.toString());
|
||||
nth++;
|
||||
}
|
||||
```
|
||||
|
||||
### for...in Loop
|
||||
|
||||
Iterate over object properties (useful for selections):
|
||||
|
||||
```javascript
|
||||
var selection = {
|
||||
"Color": "red",
|
||||
"Location": "GER"
|
||||
};
|
||||
|
||||
for (var propKey in selection) {
|
||||
var propValue = selection[propKey];
|
||||
console.log(propKey + ": " + propValue);
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: `foreach` iterators are **not supported**.
|
||||
|
||||
---
|
||||
|
||||
## Operators
|
||||
|
||||
### Equality Operators
|
||||
|
||||
Analytics Designer supports two equality operators with important differences:
|
||||
|
||||
| Operator | Name | Behavior |
|
||||
|----------|------|----------|
|
||||
| `===` | Triple equals (strict) | Compares value AND type |
|
||||
| `==` | Double equals | Only valid if both sides have same static type |
|
||||
|
||||
**Triple Equals (Recommended)**:
|
||||
|
||||
```javascript
|
||||
var aNumber = 1;
|
||||
if (aNumber === "1") {
|
||||
// FALSE: Different types (number vs string)
|
||||
}
|
||||
```
|
||||
|
||||
**Double Equals**:
|
||||
|
||||
```javascript
|
||||
var aNumber = 1;
|
||||
if (aNumber == "1") {
|
||||
// TRUE in standard JS, but ERROR in Analytics Designer
|
||||
// unless both sides have same static type
|
||||
}
|
||||
```
|
||||
|
||||
**Best Practice**: Always use triple equals (`===`) for comparisons.
|
||||
|
||||
---
|
||||
|
||||
## Built-in Objects
|
||||
|
||||
Analytics Designer supports standard JavaScript built-in objects:
|
||||
|
||||
### Math
|
||||
|
||||
```javascript
|
||||
var max = Math.max(10, 20);
|
||||
var min = Math.min(10, 20);
|
||||
var rounded = Math.round(3.7);
|
||||
var random = Math.random();
|
||||
var power = Math.pow(2, 3); // 8
|
||||
var sqrt = Math.sqrt(16); // 4
|
||||
```
|
||||
|
||||
### Date
|
||||
|
||||
```javascript
|
||||
var now = new Date();
|
||||
var year = now.getFullYear();
|
||||
var month = now.getMonth(); // 0-11
|
||||
var day = now.getDate();
|
||||
var formatted = now.toISOString();
|
||||
```
|
||||
|
||||
### Number
|
||||
|
||||
```javascript
|
||||
var num = 42;
|
||||
var str = num.toString();
|
||||
var fixed = (3.14159).toFixed(2); // "3.14"
|
||||
```
|
||||
|
||||
### String
|
||||
|
||||
```javascript
|
||||
var str = "Hello World";
|
||||
var upper = str.toUpperCase();
|
||||
var lower = str.toLowerCase();
|
||||
var sub = str.substring(0, 5); // "Hello"
|
||||
var index = str.indexOf("World"); // 6
|
||||
var replaced = str.replace("World", "SAC");
|
||||
var split = str.split(" "); // ["Hello", "World"]
|
||||
var len = str.length; // 11
|
||||
```
|
||||
|
||||
### Array
|
||||
|
||||
```javascript
|
||||
var arr = [1, 2, 3];
|
||||
arr.push(4); // Add to end
|
||||
var last = arr.pop(); // Remove from end
|
||||
var len = arr.length;
|
||||
var joined = arr.join(", ");
|
||||
var sorted = arr.sort();
|
||||
```
|
||||
|
||||
**Note**: External JavaScript libraries **cannot be imported**.
|
||||
|
||||
---
|
||||
|
||||
## Arrays
|
||||
|
||||
### One-Dimensional Arrays
|
||||
|
||||
```javascript
|
||||
// Create typed array
|
||||
var arr1D = ArrayUtils.create(Type.number);
|
||||
|
||||
// Or use literal syntax
|
||||
var myArray = [1, 2, 3];
|
||||
```
|
||||
|
||||
### Two-Dimensional Arrays
|
||||
|
||||
Analytics Designer doesn't have a direct 2D array method, but you can create one using nested 1D arrays:
|
||||
|
||||
```javascript
|
||||
var numCols = 4;
|
||||
var numRows = 3;
|
||||
|
||||
// Create first row
|
||||
var arr1D = ArrayUtils.create(Type.number);
|
||||
|
||||
// Create 2D array containing the first row
|
||||
var arr2D = [arr1D];
|
||||
|
||||
// Add remaining rows
|
||||
for (var i = 1; i < numRows; i++) {
|
||||
arr2D.push(ArrayUtils.create(Type.number));
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: You cannot use `var arr2D = []` because Analytics Designer cannot infer the content type from an empty array.
|
||||
|
||||
### Setting Values in 2D Array
|
||||
|
||||
```javascript
|
||||
arr2D[row][col] = value;
|
||||
|
||||
// Example: Fill with sequential numbers
|
||||
var count = 0;
|
||||
for (var row = 0; row < numRows; row++) {
|
||||
for (var col = 0; col < numCols; col++) {
|
||||
arr2D[row][col] = count;
|
||||
count = count + 1;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Getting Values from 2D Array
|
||||
|
||||
```javascript
|
||||
for (var row = 0; row < numRows; row++) {
|
||||
for (var col = 0; col < numCols; col++) {
|
||||
console.log(arr2D[row][col].toString());
|
||||
}
|
||||
console.log(""); // Row separator
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Method Chaining
|
||||
|
||||
Chain method calls for compact code:
|
||||
|
||||
```javascript
|
||||
// Without chaining (verbose)
|
||||
var theDataSource = Chart_1.getDataSource();
|
||||
var theVariables = theDataSource.getVariables();
|
||||
console.log(theVariables);
|
||||
|
||||
// With chaining (compact)
|
||||
console.log(Chart_1.getDataSource().getVariables());
|
||||
```
|
||||
|
||||
**Use Cases**:
|
||||
- Debug logging
|
||||
- One-time data retrieval
|
||||
- Reducing visual clutter
|
||||
|
||||
**When NOT to Chain**:
|
||||
- When you need to reuse intermediate results
|
||||
- When debugging complex operations
|
||||
|
||||
---
|
||||
|
||||
## Script Runtime Security
|
||||
|
||||
Analytics Designer validates scripts before execution to prevent security risks.
|
||||
|
||||
### What Is Blocked
|
||||
|
||||
The execution is isolated to prevent:
|
||||
|
||||
- **DOM access**: Cannot manipulate HTML elements
|
||||
- **Global variable access**: Cannot access browser globals
|
||||
- **Prototype modification**: Cannot alter built-in prototypes
|
||||
- **Network requests**: Cannot send arbitrary HTTP requests
|
||||
- **Script imports**: Cannot load external JavaScript
|
||||
- **ActiveX/plugins**: Cannot use browser plugins
|
||||
- **Web Workers**: Cannot spawn additional workers
|
||||
- **Cookies**: Cannot access browser cookies
|
||||
- **Cross-domain access**: Enforced same-origin policies
|
||||
|
||||
### Validation
|
||||
|
||||
- Same validation logic as script editor
|
||||
- Not all validations performed at runtime (e.g., analytic data validation)
|
||||
- Only allowed JavaScript subset can execute
|
||||
- Critical features use secured alternative APIs
|
||||
|
||||
---
|
||||
|
||||
## The `this` Keyword
|
||||
|
||||
Use `this` to reference the current widget or script object:
|
||||
|
||||
```javascript
|
||||
// In Chart_1 onSelect event
|
||||
var theDataSource = this.getDataSource();
|
||||
console.log(theDataSource.getVariables());
|
||||
|
||||
// Equivalent to:
|
||||
var theDataSource = Chart_1.getDataSource();
|
||||
console.log(theDataSource.getVariables());
|
||||
```
|
||||
|
||||
**Usage Contexts**:
|
||||
- Within widget scripts: `this` = the widget instance
|
||||
- Within script object functions: `this` = the script object
|
||||
- Explicitly reference parent: Use widget name (`Chart_1`)
|
||||
- Either approach is valid (stylistic choice)
|
||||
|
||||
---
|
||||
|
||||
## Code Completion and Value Help
|
||||
|
||||
The script editor provides intelligent assistance:
|
||||
|
||||
### Code Completion
|
||||
|
||||
- **Standard completion**: Complete local/global identifiers
|
||||
- **Semantic completion**: Suggest member functions
|
||||
- **Fuzzy matching**: Find widgets even with typos (e.g., "cose" → "console")
|
||||
|
||||
### Value Help
|
||||
|
||||
Context-aware value proposals:
|
||||
- Measures of a data source for function parameters
|
||||
- Dimension members for filter methods
|
||||
- Widget names in the application
|
||||
|
||||
### Keyboard Shortcut
|
||||
|
||||
Press `CTRL+Spacebar` to:
|
||||
- Auto-complete if only one valid option
|
||||
- Display value help list if multiple options
|
||||
|
||||
---
|
||||
|
||||
## Accessing Objects
|
||||
|
||||
Every object in the Outline (widgets, script variables, script objects) is a global object accessible by name:
|
||||
|
||||
```javascript
|
||||
// Access widget
|
||||
var isVisible = Chart_1.isVisible();
|
||||
|
||||
// Access script variable
|
||||
var currentValue = ScriptVariable_1;
|
||||
|
||||
// Access script object function
|
||||
ScriptObject_1.myFunction();
|
||||
```
|
||||
|
||||
### Finding Widgets with Fuzzy Matching
|
||||
|
||||
Type partial names and use CTRL+Spacebar:
|
||||
- Completes automatically if unique match
|
||||
- Shows list if multiple matches
|
||||
- Works even with typos
|
||||
|
||||
---
|
||||
|
||||
## External Resources
|
||||
|
||||
- **API Reference**: [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
- **Scripting Documentation**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/6a4db9a9c8634bcb86cecbf1f1dbbf8e.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/6a4db9a9c8634bcb86cecbf1f1dbbf8e.html)
|
||||
|
||||
---
|
||||
|
||||
**Source**: SAP Analytics Designer Development Guide - Chapter 4: Scripting in Analytics Designer
|
||||
**Last Updated**: 2025-11-23
|
||||
32
references/search-to-insight.md
Normal file
32
references/search-to-insight.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Search to Insight Technical Object (2025.23)
|
||||
|
||||
Source: `help-portal-8a5c547a501640c38ad549109d4314b1.md` (Work with Search to Insight in Analytic Applications).
|
||||
|
||||
## Data support
|
||||
- Indexed live HANA models and acquired models (with access).
|
||||
- Dimensions without/with data access control (respects user access).
|
||||
|
||||
## Modes
|
||||
- **Simple mode**: natural-language questions for users with limited model knowledge.
|
||||
- **Advanced mode**: switch models and select dimensions during queries.
|
||||
|
||||
## Create technical object
|
||||
1. In **Scripting** section of Outline, select the icon next to Search to Insight.
|
||||
2. Side panel opens with default name `SearchToInsight_1`.
|
||||
3. Under **Models to Search**, add one or more models.
|
||||
4. Click **Done** to save.
|
||||
|
||||
## Key APIs / workflows
|
||||
- Open/close the Search to Insight dialog.
|
||||
- `applySearchToChart()` to apply a question result to a chart.
|
||||
- Variable management APIs: save variable values; reuse when calling `applySearchToChart()`.
|
||||
|
||||
## Use cases
|
||||
- Start different modes (simple/advanced).
|
||||
- Receive questions from host HTML page and apply to charts.
|
||||
- Persist and reapply variables together with search results.
|
||||
|
||||
## Implementation notes
|
||||
- Create the technical object before users can query.
|
||||
- Multiple models can be added to the search scope.
|
||||
|
||||
12
references/sliders.md
Normal file
12
references/sliders.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Sliders & Range Sliders (2025.23)
|
||||
|
||||
Source: `help-portal-9a00daeb47ab4264a278d9928afb95da.md`.
|
||||
|
||||
## Overview
|
||||
Use sliders/range sliders to let users adjust numeric values.
|
||||
|
||||
## Guidance
|
||||
- Bind slider values to variables or widget properties.
|
||||
- Handle change events to refresh data or recalculate KPIs.
|
||||
- Configure min/max/step to match business rules.
|
||||
|
||||
21
references/smart-grouping.md
Normal file
21
references/smart-grouping.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Smart Grouping for Charts (2025.23)
|
||||
|
||||
Source: `help-portal-fa8a42c6f906486897653e2f5e3708e3.md` (Apply Smart Grouping to Charts).
|
||||
|
||||
## Scope
|
||||
- Bubble and scatterplot charts for correlation analysis.
|
||||
|
||||
## Prerequisite
|
||||
- Enable smart grouping in the chart’s Builder panel before scripting.
|
||||
|
||||
## APIs
|
||||
- Set number of groups (example):
|
||||
```javascript
|
||||
Chart_1.setSmartGroupingNumberOfGroups(5);
|
||||
```
|
||||
- Additional controls: enable/disable smart grouping, allow user-defined group labels, include/exclude tooltip measures.
|
||||
|
||||
## Implementation notes
|
||||
- Designed for automated grouping of similar data points.
|
||||
- Use Analytics Designer API Reference for full parameter list.
|
||||
|
||||
14
references/text-pool-tech-object.md
Normal file
14
references/text-pool-tech-object.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Text Pool Technical Object (2025.23)
|
||||
|
||||
Source: `help-portal-da0de4bbb64b42f39de50de885d4007c.md`.
|
||||
|
||||
## Purpose
|
||||
Centralize text resources for reuse across the application.
|
||||
|
||||
## Capabilities
|
||||
- Manage multilingual text entries programmatically.
|
||||
- Retrieve and apply texts in scripts to widgets.
|
||||
|
||||
## Notes
|
||||
- Use to avoid hard-coded strings and support localization.
|
||||
|
||||
20
references/text-widget.md
Normal file
20
references/text-widget.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Text Widget & Dynamic Text (2025.23)
|
||||
|
||||
Source: `help-portal-bf11f30235854d6d852d00addb7fd1f6.md` (Use Text Widgets in Analytic Applications).
|
||||
|
||||
## Dynamic text sources
|
||||
- Analytic application properties.
|
||||
- Input controls.
|
||||
- Tile filters & variables.
|
||||
- Model variables.
|
||||
- **Script variables** (Analytics Designer specific; values update automatically at runtime).
|
||||
|
||||
## APIs
|
||||
- `getPlainText()` – returns text with line breaks preserved.
|
||||
- `applyText()` – sets text; use `\n` for line breaks.
|
||||
|
||||
## Notes
|
||||
- Text widget available in stories and analytic applications.
|
||||
- Add via toolbar → **Text**.
|
||||
- Script variables enable fully dynamic text scenarios.
|
||||
|
||||
21
references/time-series-forecast.md
Normal file
21
references/time-series-forecast.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Time Series Forecast APIs (2025.23)
|
||||
|
||||
Source: `help-portal-366bdc2291e347bfbedff1bbf9377fca.md` (Work with Time Series Forecast in Charts).
|
||||
|
||||
## Purpose
|
||||
Enable predictive time series forecasts on time-series or line charts within analytic applications.
|
||||
|
||||
## Core API
|
||||
```javascript
|
||||
// Enable automatic forecast on a chart
|
||||
Chart_1.getDataSource().setForecastEnabled(true);
|
||||
```
|
||||
|
||||
## Options
|
||||
- Configure number of forecast periods via API.
|
||||
|
||||
## Implementation notes
|
||||
- Algorithm runs on historical data for selected measure.
|
||||
- Works with time series and line charts.
|
||||
- Use Analytics Designer API Reference for additional parameters.
|
||||
|
||||
14
references/timer-tech-object.md
Normal file
14
references/timer-tech-object.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Timer Technical Object (2025.23)
|
||||
|
||||
Source: `help-portal-142a436d0f2a4e97a9af15853ca9b739.md`.
|
||||
|
||||
## Purpose
|
||||
Schedule time-based actions within analytic applications.
|
||||
|
||||
## Capabilities
|
||||
- Start/stop timers, handle timer events to trigger scripts.
|
||||
- Use for periodic refresh, animations, or delayed actions.
|
||||
|
||||
## Notes
|
||||
- Avoid excessive timer frequency to reduce backend calls and UI churn.
|
||||
|
||||
16
references/value-help.md
Normal file
16
references/value-help.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Value Help in Script Editor (2025.23)
|
||||
|
||||
Source: `help-portal-407bdedb4fd74f3aa2a3bd7b8b8d812e.md`.
|
||||
|
||||
## How to invoke
|
||||
- Press `CTRL + SPACE` in the script editor for context-aware suggestions.
|
||||
|
||||
## Contexts
|
||||
- **Measures**: opens measure metadata selector.
|
||||
- **Variable values**: for dimension variables, opens member selector; non-dimension variables show values in completion list.
|
||||
- **Selection objects**: offers keys (dimensions) and values (dimension members) for the associated data source; works after dot access on selection object.
|
||||
|
||||
## Notes
|
||||
- Use value help to avoid typos in member IDs, variables, and selection object properties.
|
||||
- For dimension properties, value help includes member values for comparisons/assignments.
|
||||
|
||||
130
references/whats-new-2025.23.md
Normal file
130
references/whats-new-2025.23.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# What's New in SAC Scripting (2025.23)
|
||||
|
||||
Complete overview of new scripting features and updates in SAP Analytics Cloud 2025.23.
|
||||
|
||||
**Release Date**: November 3, 2025
|
||||
**SAC Version**: 2025.23
|
||||
|
||||
---
|
||||
|
||||
## Major New Features
|
||||
|
||||
### Time Series Forecast API
|
||||
- **File**: `references/time-series-forecast.md`
|
||||
- **Purpose**: Enable time-series forecasting on line and time-series charts
|
||||
- **Key Methods**:
|
||||
- `Chart.getTimeSeriesForecastEnabled()`
|
||||
- `Chart.setTimeSeriesForecastEnabled(boolean)`
|
||||
- `Chart.getTimeSeriesForecastOptions()`
|
||||
- `Chart.setTimeSeriesForecastOptions(options)`
|
||||
|
||||
### Search to Insight Technical Object
|
||||
- **File**: `references/search-to-insight.md`
|
||||
- **Purpose**: Integrate natural language search with visual analytics
|
||||
- **Key Features**:
|
||||
- Dialog-based search interface
|
||||
- Apply search results to charts
|
||||
- Technical object for programmatic control
|
||||
|
||||
### Comments APIs
|
||||
- **File**: `references/comments.md`
|
||||
- **Purpose**: Add, view, and manage comments on widgets and table cells
|
||||
- **Key Features**:
|
||||
- Widget-level comments
|
||||
- Table cell comments
|
||||
- Threaded comment support
|
||||
- Comment synchronization
|
||||
|
||||
### Smart Grouping
|
||||
- **File**: `references/smart-grouping.md`
|
||||
- **Purpose**: Automatic grouping of data points in bubble and scatter charts
|
||||
- **Key Features**:
|
||||
- ML-driven grouping algorithms
|
||||
- Configurable grouping parameters
|
||||
- Visual group indicators
|
||||
|
||||
### Explorer & Smart Insights
|
||||
- **File**: `references/explorer-smart-insights.md`
|
||||
- **Purpose**: Advanced data exploration and AI-powered insights
|
||||
- **Key Features**:
|
||||
- Launch Explorer programmatically
|
||||
- Apply Smart Insights to visualizations
|
||||
- Context-aware analysis
|
||||
|
||||
### Geo Map Enhancements
|
||||
- **File**: `references/geomap.md`
|
||||
- **Purpose**: Enhanced geographical visualization capabilities
|
||||
- **Key Features**:
|
||||
- Quick menu implementation
|
||||
- Scripted layer control
|
||||
- Improved interaction patterns
|
||||
|
||||
### Composite Scripting
|
||||
- **Files**:
|
||||
- `references/composites-scripting.md`
|
||||
- `references/composites-restrictions.md`
|
||||
- **Purpose**: Scripting within composite widgets
|
||||
- **Key Features**:
|
||||
- Event handling in composites
|
||||
- Cross-widget communication
|
||||
- Known limitations and workarounds
|
||||
|
||||
### Data Blending Updates
|
||||
- **File**: `references/blending-limitations.md`
|
||||
- **Purpose**: Updated support for blended data models
|
||||
- **Key Updates**:
|
||||
- Expanded API coverage
|
||||
- New blending scenarios supported
|
||||
- Performance improvements
|
||||
|
||||
### Analytics Designer Enhancements
|
||||
- **File**: `references/analytics-designer-restrictions.md`
|
||||
- **Purpose**: Updated list of known restrictions
|
||||
- **Key Updates**:
|
||||
- Reduced restrictions in 2025.23
|
||||
- New capabilities added
|
||||
- Migration guidance
|
||||
|
||||
---
|
||||
|
||||
## Documentation Updates
|
||||
|
||||
### Help Portal Updates
|
||||
- **File**: `references/auth-required.md`
|
||||
- **Note**: Some Help Portal articles now require SAP login
|
||||
- **Impact**: Direct links may prompt for authentication
|
||||
|
||||
### API Reference
|
||||
- Documentation version updated to reflect new methods
|
||||
- Enhanced examples for all new features
|
||||
- Performance optimization guidance
|
||||
|
||||
---
|
||||
|
||||
## Migration Considerations
|
||||
|
||||
### For Developers
|
||||
1. Review existing code for deprecated patterns
|
||||
2. Test new features in development environment
|
||||
3. Update error handling for new APIs
|
||||
4. Consider performance implications of new features
|
||||
|
||||
### For Administrators
|
||||
1. Plan upgrade to SAC 2025.23
|
||||
2. Update user training materials
|
||||
3. Review security implications of new features
|
||||
4. Test compatibility with existing models
|
||||
|
||||
---
|
||||
|
||||
## Related Resources
|
||||
|
||||
- [SAP Analytics Cloud Release Notes](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD)
|
||||
- [Analytics Designer API Reference](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/latest/en-US/index.html)
|
||||
- [Optimized Story Experience API Reference](https://help.sap.com/doc/1639cb9ccaa54b2592224df577abe822/latest/en-US/index.html)
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-11-27
|
||||
**SAC Version**: 2025.23
|
||||
**Documentation Version**: 1.0.0
|
||||
Reference in New Issue
Block a user