From b82cab49fc8c7b86ced206c7bcb221962a1113e7 Mon Sep 17 00:00:00 2001 From: Zhongwei Li Date: Sun, 30 Nov 2025 08:55:33 +0800 Subject: [PATCH] Initial commit --- .claude-plugin/plugin.json | 12 + README.md | 3 + SKILL.md | 184 ++++ SKILL.md.backup | 838 ++++++++++++++++++ plugin.lock.json | 269 ++++++ references/analytics-designer-restrictions.md | 52 ++ references/api-advanced-widgets.md | 793 +++++++++++++++++ references/api-application.md | 630 +++++++++++++ references/api-calendar-bookmarks.md | 603 +++++++++++++ references/api-data-operations.md | 526 +++++++++++ references/api-datasource.md | 463 ++++++++++ references/api-planning.md | 541 +++++++++++ references/api-widgets.md | 622 +++++++++++++ references/auth-required.md | 16 + references/automatic-refactoring.md | 15 + references/best-practices-developer.md | 304 +++++++ references/best-practices-planning-stories.md | 367 ++++++++ references/bind-widget-values.md | 16 + references/blending-limitations.md | 26 + references/bookmark-set-tech-object.md | 11 + references/bookmark-settings.md | 15 + .../calendar-integration-tech-object.md | 14 + references/check-errors.md | 14 + references/check-references.md | 15 + references/comments.md | 20 + references/composites-restrictions.md | 42 + references/composites-scripting.md | 18 + references/data-actions-tech-object.md | 640 +++++++++++++ .../data-change-insights-tech-object.md | 13 + references/debug-scripts.md | 19 + references/debugging-browser-tools.md | 425 +++++++++ references/explorer-smart-insights.md | 33 + references/export-pdf-tech-object.md | 14 + references/export-ppt-tech-object.md | 13 + references/geomap.md | 26 + .../iframe-embedding-lumira-migration.md | 314 +++++++ references/input-fields.md | 15 + references/memberinfo-performance.md | 11 + references/multi-actions-tech-object.md | 14 + references/odata-service.md | 14 + references/on-after-data-entry-process.md | 14 + references/optimize-type-libraries.md | 14 + references/pattern-functions.md | 15 + references/planning-model-tech-object.md | 14 + references/script-editor.md | 21 + references/script-objects.md | 17 + references/script-performance-popup.md | 15 + references/script-variables.md | 20 + references/scripting-language-fundamentals.md | 509 +++++++++++ references/search-to-insight.md | 32 + references/sliders.md | 12 + references/smart-grouping.md | 21 + references/text-pool-tech-object.md | 14 + references/text-widget.md | 20 + references/time-series-forecast.md | 21 + references/timer-tech-object.md | 14 + references/value-help.md | 16 + references/whats-new-2025.23.md | 130 +++ templates/common-patterns.js | 831 +++++++++++++++++ templates/planning-operations.js | 587 ++++++++++++ 60 files changed, 10317 insertions(+) create mode 100644 .claude-plugin/plugin.json create mode 100644 README.md create mode 100644 SKILL.md create mode 100644 SKILL.md.backup create mode 100644 plugin.lock.json create mode 100644 references/analytics-designer-restrictions.md create mode 100644 references/api-advanced-widgets.md create mode 100644 references/api-application.md create mode 100644 references/api-calendar-bookmarks.md create mode 100644 references/api-data-operations.md create mode 100644 references/api-datasource.md create mode 100644 references/api-planning.md create mode 100644 references/api-widgets.md create mode 100644 references/auth-required.md create mode 100644 references/automatic-refactoring.md create mode 100644 references/best-practices-developer.md create mode 100644 references/best-practices-planning-stories.md create mode 100644 references/bind-widget-values.md create mode 100644 references/blending-limitations.md create mode 100644 references/bookmark-set-tech-object.md create mode 100644 references/bookmark-settings.md create mode 100644 references/calendar-integration-tech-object.md create mode 100644 references/check-errors.md create mode 100644 references/check-references.md create mode 100644 references/comments.md create mode 100644 references/composites-restrictions.md create mode 100644 references/composites-scripting.md create mode 100644 references/data-actions-tech-object.md create mode 100644 references/data-change-insights-tech-object.md create mode 100644 references/debug-scripts.md create mode 100644 references/debugging-browser-tools.md create mode 100644 references/explorer-smart-insights.md create mode 100644 references/export-pdf-tech-object.md create mode 100644 references/export-ppt-tech-object.md create mode 100644 references/geomap.md create mode 100644 references/iframe-embedding-lumira-migration.md create mode 100644 references/input-fields.md create mode 100644 references/memberinfo-performance.md create mode 100644 references/multi-actions-tech-object.md create mode 100644 references/odata-service.md create mode 100644 references/on-after-data-entry-process.md create mode 100644 references/optimize-type-libraries.md create mode 100644 references/pattern-functions.md create mode 100644 references/planning-model-tech-object.md create mode 100644 references/script-editor.md create mode 100644 references/script-objects.md create mode 100644 references/script-performance-popup.md create mode 100644 references/script-variables.md create mode 100644 references/scripting-language-fundamentals.md create mode 100644 references/search-to-insight.md create mode 100644 references/sliders.md create mode 100644 references/smart-grouping.md create mode 100644 references/text-pool-tech-object.md create mode 100644 references/text-widget.md create mode 100644 references/time-series-forecast.md create mode 100644 references/timer-tech-object.md create mode 100644 references/value-help.md create mode 100644 references/whats-new-2025.23.md create mode 100644 templates/common-patterns.js create mode 100644 templates/planning-operations.js diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..1903f97 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "sap-sac-scripting", + "description": "Scripting for SAC Analytics Designer and stories. Covers DataSource API, Chart/Table manipulation, Planning operations, Calendar integration, and 39 code templates.", + "version": "1.1.0", + "author": { + "name": "Zhongwei Li", + "email": "zhongweili@tubi.tv" + }, + "skills": [ + "./" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..37a7e11 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# sap-sac-scripting + +Scripting for SAC Analytics Designer and stories. Covers DataSource API, Chart/Table manipulation, Planning operations, Calendar integration, and 39 code templates. diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..c5b429b --- /dev/null +++ b/SKILL.md @@ -0,0 +1,184 @@ +--- +name: sap-sac-scripting +description: | + Comprehensive SAC scripting skill for Analytics Designer and Optimized Story Experience. Use when building analytic applications, planning models, or enhanced stories. Covers DataSource API, Chart/Table manipulation, Planning operations, Calendar integration, Bookmarks, Timer API, Container widgets, Layout API, R Visualizations, Custom Widgets, Navigation, variables, event handlers, debugging, performance optimization, and 2025.23 features: comments APIs, Search to Insight, smart grouping, time-series forecast, geo map quick menus, Explorer/Smart Insights, composites scripting. Includes 40 code templates. +license: GPL-3.0 +metadata: + version: 1.7.0 + last_verified: 2025-11-27 + sac_version: "2025.23" + api_reference_version: "2025.14" + documentation_source: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD) + reference_files: 52 + template_patterns: 40 + status: production +--- + +# SAP Analytics Cloud Scripting + +Comprehensive skill for scripting in SAP Analytics Cloud (SAC) Analytics Designer and Optimized Story Experience. + +## What's New in SAC 2025.23 + +Time series forecast API, Search to Insight, comments APIs, smart grouping, Explorer & Smart Insights, geo map enhancements, and composite scripting support. See `references/whats-new-2025.23.md` for complete details. + +## When to Use This Skill + +Use this skill when working on tasks involving: + +- **Analytics Designer Development**: Creating interactive dashboards, planning applications, event handlers +- **Optimized Story Experience**: Enhancing stories with scripting capabilities +- **Data Operations**: Filtering, data access, hierarchies, data actions +- **Planning Operations**: Version management, data locking, workflows +- **UI/UX Enhancements**: Popups, navigation, responsive design +- **Advanced Features**: Calendar integration, bookmarks, R visualizations + +## Quick Start + +### Script Editor Access +- **Analytics Designer**: Edit mode → Select widget → Scripts tab +- **Optimized Story Experience**: Advanced Mode → Select widget → Add script + +### Basic Script Structure +```javascript +// Event handler example +var selections = Chart_1.getSelections(); +if (selections.length > 0) { + Table_1.getDataSource().setDimensionFilter("Location", selections[0]["Location"]); +} +``` + +## Core APIs + +### DataSource API +- **Reference**: `Chart_1.getDataSource()` or `Table_1.getDataSource()` +- **Key Methods**: `getMembers()`, `getData()`, `setDimensionFilter()`, `refreshData()` +- **Performance**: Use `getResultSet()` (no backend) instead of `getMembers()` (hits backend) + +### Planning API +- **Access**: `Table_1.getPlanning()` +- **Operations**: Version management (`getPublicVersion()`, `publish()`, `copy()`) +- **Data Locking**: Check/modify lock states + +### Widget APIs +- **Charts**: `addDimension()`, `addMeasure()`, `getSelections()` +- **Tables**: `addDimensionToRows()`, `setZeroSuppressionEnabled()` +- **Containers**: Panel, TabStrip, PageBook for layout management + +### Application Object +- **Utilities**: `showBusyIndicator()`, `showMessage()` +- **Info**: `getInfo()`, `getUserInfo()`, `getTheme()` + +## Common Patterns + +### Filter Based on Selection +```javascript +var selections = Chart_1.getSelections(); +if (selections.length > 0) { + Table_1.getDataSource().setDimensionFilter("Location", selections[0]["Location"]); +} +``` + +### Pause/Resume Refresh (Performance) +```javascript +ds.setRefreshPaused(true); +// Apply multiple operations +ds.setRefreshPaused(false); // Single backend call +``` + +### Get Booked Values Only +```javascript +var members = ds.getMembers("Dimension", {accessMode: MemberAccessMode.BookedValues}); +``` + +## Debugging + +### Console Logging +```javascript +console.log("Debug info:", myVariable); +console.log("Selections:", Chart_1.getSelections()); +``` + +### Browser Tools +- Open with F12 → Console tab +- Filter by "Info" type +- Look for "sandbox.worker.main.*.js" + +### Performance Logging +Add URL parameter: `?APP_PERFORMANCE_LOGGING=true` + +## Performance Best Practices + +1. **Minimize Backend Trips**: Use `getResultSet()` over `getMembers()` +2. **Batch Operations**: Pause refresh, apply changes, resume +3. **Cache References**: Store `getDataSource()` in variables +4. **Empty onInitialization**: Avoid heavy startup operations + +## Developer Best Practices + +### Naming Conventions +- Charts: `chartB_revenue` (Bar), `chartL_sales` (Line) +- Tables: `tbl_transactions` +- Panels: `pnl_header` +- Buttons: `btn_export_pdf` + +### Script Annotation +```javascript +/* + * Script: onSelect - chartB_revenue_by_region + * Purpose: Filter detail table when user selects a region + */ +``` + +## Events + +### Application Events +- `onInitialization`: Runs once on load (keep empty!) +- `onResize`: Application resize +- `onOrientationChange`: Mobile orientation change + +### Widget Events +- `onSelect`: Data point selection (Chart/Table) +- `onResultChanged`: Data changes +- `onClick`: Button click + +## Planning Story Architecture + +### Multi-Story Pattern +``` +Planning_Application/ +├── 00_Entry_Point.story +├── 01_Configuration.story +├── 02_Plan_FTE.story +├── 03_Plan_Costs.story +└── 04_Reports.story +``` + +### Navigation Script +```javascript +var urlParameters = ArrayUtils.create(Type.UrlParameter); +urlParameters.push(UrlParameter.create("page", "0")); +NavigationUtils.openStory(storyId, "", urlParameters, false); +``` + +## Bundled Resources + +**Reference Files** (52 files): +- Core APIs: `references/api-datasource.md`, `references/api-widgets.md` +- Advanced: `references/api-calendar-bookmarks.md`, `references/api-advanced-widgets.md` +- Best Practices: `references/best-practices-developer.md` +- Language: `references/scripting-language-fundamentals.md` + +**Templates** (40 patterns): +- `templates/common-patterns.js`: 40 scripting patterns +- `templates/planning-operations.js`: Planning-specific patterns + +## Official Documentation + +- **Analytics Designer API**: [Analytics Designer API](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/latest/en-US/) +- **Optimized Story Experience API**: [Optimized Story Experience API](https://help.sap.com/doc/1639cb9ccaa54b2592224df577abe822/latest/en-US/) +- **SAC Scripting Docs**: [SAC Scripting Docs](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD) + +--- + +**Version**: 1.7.0 | **Last Verified**: 2025-11-27 | **SAC Version**: 2025.23 diff --git a/SKILL.md.backup b/SKILL.md.backup new file mode 100644 index 0000000..f723786 --- /dev/null +++ b/SKILL.md.backup @@ -0,0 +1,838 @@ +--- +name: sap-sac-scripting +description: | + Comprehensive SAC scripting skill for Analytics Designer and Optimized Story Experience. Use when building analytic applications, planning models, or enhanced stories. Covers DataSource API, Chart/Table manipulation, Planning operations, Calendar integration, Bookmarks, Timer API, Container widgets, Layout API, R Visualizations, Custom Widgets, Navigation, variables, event handlers, debugging, performance optimization, and 2025.23 features: comments APIs, Search to Insight, smart grouping, time-series forecast, geo map quick menus, Explorer/Smart Insights, composites scripting. Includes 40 code templates. +license: GPL-3.0 +metadata: + version: 1.7.0 + last_verified: 2025-11-27 + sac_version: "2025.23" + api_reference_version: "2025.14" + documentation_source: https://help.sap.com/docs/SAP_ANALYTICS_CLOUD + reference_files: 52 + template_patterns: 40 + status: production +--- + +# SAP Analytics Cloud Scripting Skill + +Comprehensive skill for scripting in SAP Analytics Cloud (SAC) Analytics Designer and Optimized Story Experience. + +--- + +## What’s New (2025.23) +- Time series forecast API for time-series/line charts (`references/time-series-forecast.md`). +- Search to Insight technical object with dialog/apply-to-chart APIs (`references/search-to-insight.md`). +- Widget/table comments APIs (`references/comments.md`). +- Smart grouping for bubble/scatter charts (`references/smart-grouping.md`). +- Explorer & Smart Insights launch/apply patterns (`references/explorer-smart-insights.md`). +- Geo map quick menus and scripting hooks (`references/geomap.md`). +- Composite scripting and composite feature restrictions (`references/composites-scripting.md`, `references/composites-restrictions.md`). +- Blending limitations and Analytics Designer known restrictions (`references/blending-limitations.md`, `references/analytics-designer-restrictions.md`). +- Authentication-required Help Portal page logged for awareness (`references/auth-required.md`). + +--- + +## When to Use This Skill + +Use this skill when working on tasks involving: + +**Analytics Designer Development**: +- Creating analytic applications with interactive dashboards +- Building planning applications for data entry and forecasting +- Writing event handler scripts (onInitialization, onSelect, onResultChanged) +- Implementing widget interactions (charts, tables, input controls) +- Managing data sources and applying filters programmatically + +**Optimized Story Experience**: +- Enhancing stories with scripting capabilities +- Adding interactivity to existing stories +- Implementing custom business logic +- Creating dynamic visualizations + +**Data Operations**: +- Filtering and manipulating data sources +- Reading dimension members and measure values +- Working with hierarchies and selections +- Implementing data actions and multi-actions + +**Planning Operations**: +- Managing planning versions (public/private) +- Publishing and copying versions +- Handling data locking +- Implementing data entry workflows + +**UI/UX Enhancements**: +- Creating popups and dialogs +- Managing widget visibility +- Implementing dynamic navigation +- Building responsive applications +- Using container widgets (Panel, TabStrip, PageBook, Flow Layout) +- Implementing Layout API for dynamic sizing/positioning +- Adding/exposing comments on widgets and table cells (`references/comments.md`) +- Applying dynamic text via script variables (`references/text-widget.md`) +- Using geo maps with quick menu control and scripted layers (`references/geomap.md`) +- Binding widget values to variables (`references/bind-widget-values.md`) +- Using input fields/text areas and sliders/range sliders (`references/input-fields.md`, `references/sliders.md`) +- Bookmark settings, APIs, and bookmark sets (`references/bookmark-settings.md`, `references/bookmark-set-tech-object.md`) + +**Calendar & Workflow Integration**: +- Creating and managing calendar tasks and processes +- Implementing approval workflows (submit, approve, reject) +- Adding reminders and notifications +- Integrating with planning calendar events + +**Bookmarks & State Management**: +- Saving and restoring application state +- Creating global and personal bookmarks +- URL parameter integration +- Linked Analysis for cross-widget filtering + +**Advanced Features**: +- Timer-based operations and animations +- R Visualizations for specialized charts +- Custom Widget development +- Cross-application navigation +- Search to Insight technical object, dialog control, apply-to-chart (`references/search-to-insight.md`) +- Time-series forecast enablement on charts (`references/time-series-forecast.md`) +- Smart grouping for correlation charts (`references/smart-grouping.md`) +- Explorer & Smart Insights launch and application patterns (`references/explorer-smart-insights.md`) +- Scripting inside composites (`references/composites-scripting.md`) +- Pattern-based functions for ML-driven text pattern detection (`references/pattern-functions.md`) + +**Technical Objects & Actions**: +- Data actions, multi actions, and planning model technical objects (`references/data-actions-tech-object.md`, `references/multi-actions-tech-object.md`, `references/planning-model-tech-object.md`) +- Calendar integration, data change insights, timer, text pool (`references/calendar-integration-tech-object.md`, `references/data-change-insights-tech-object.md`, `references/timer-tech-object.md`, `references/text-pool-tech-object.md`) +- Export technical objects for PDF/PowerPoint (`references/export-pdf-tech-object.md`, `references/export-ppt-tech-object.md`) +- OData service integration (`references/odata-service.md`) +- Bookmark set technical object (`references/bookmark-set-tech-object.md`) +- onAfterDataEntryProcess event handling for tables (`references/on-after-data-entry-process.md`) +- MemberInfo performance guidance (`references/memberinfo-performance.md`) + +**Debugging & Optimization**: +- Debugging scripts with console.log and debugger statement +- Optimizing application performance +- Troubleshooting runtime errors +- Script editor basics (`references/script-editor.md`) and value help (`references/value-help.md`) +- Optimize type libraries and automatic refactoring tools (`references/optimize-type-libraries.md`, `references/automatic-refactoring.md`) +- Check errors/references and debug mode with breakpoints (`references/check-errors.md`, `references/check-references.md`, `references/debug-scripts.md`) +- Script performance popup for runtime profiling (`references/script-performance-popup.md`) + +--- + +## Quick Start + +### Script Editor Access + +1. **Analytics Designer**: Open application in edit mode → Select widget → Click Scripts tab +2. **Optimized Story Experience**: Enable Advanced Mode → Select widget → Add script + +### Basic Script Structure + +```javascript +// Event handler script (e.g., onSelect of a chart) +var selections = Chart_1.getSelections(); +if (selections.length > 0) { + var selectedMember = selections[0]["Location"]; + Table_1.getDataSource().setDimensionFilter("Location", selectedMember); +} +``` + +### Script Types + +1. **Event Handlers**: Triggered by widget events (onSelect, onClick, onResultChanged) +2. **Application Events**: onInitialization, onResize, onOrientationChange +3. **Script Objects**: Reusable functions callable from any event handler + +--- + +## Core Concepts + +### 1. Variables + +**Local Variables** (within event handler): +```javascript +var myString = "Hello"; +var myNumber = 42; +var myBoolean = true; +var myArray = ["a", "b", "c"]; +``` + +**Global Variables** (across entire application): +- Create via Outline panel → Global Variables → (+) +- Types: String, Integer, Number, Boolean (can be arrays) +- Access from any script: `GlobalVariable_1 = "value";` + +**URL Parameters**: +- Prefix global variable name with `p_` to pass via URL +- URL format: `?p_myVariable=value` + +### 2. DataSource API + +Central API for data access. Get reference via widgets: + +```javascript +var ds = Chart_1.getDataSource(); +var ds = Table_1.getDataSource(); +``` + +**Key Methods**: +| Method | Description | +|--------|-------------| +| `getDimensions()` | Returns all dimensions | +| `getMeasures()` | Returns all measures | +| `getMembers(dimensionId, options)` | Returns dimension members | +| `getData(selection)` | Returns data values | +| `getResultSet()` | Returns current result set (no backend trip) | +| `setDimensionFilter(dimension, value)` | Sets filter on dimension | +| `removeDimensionFilter(dimension)` | Removes filter | +| `getDimensionFilters(dimension)` | Gets current filters | +| `refreshData()` | Refreshes data from backend | +| `getVariables()` | Returns model variables | +| `setVariableValue(variable, value)` | Sets variable value | + +**Reference**: See `references/api-datasource.md` for complete DataSource API. + +### 3. Widget APIs + +**Chart**: +```javascript +Chart_1.addDimension(Feed.CategoryAxis, "Location"); +Chart_1.addMeasure(Feed.ValueAxis, "[Account].[parentId].&[Revenue]"); +Chart_1.removeMember(Feed.ValueAxis, measureId); +var selections = Chart_1.getSelections(); +Chart_1.setVisible(true); +``` + +**Table**: +```javascript +Table_1.addDimensionToRows("Product"); +Table_1.addDimensionToColumns("Date"); +Table_1.setZeroSuppressionEnabled(true); +var selections = Table_1.getSelections(); +``` + +**Reference**: See `references/api-widgets.md` for complete widget APIs. + +### 4. Planning API + +Access via table widget: +```javascript +var planning = Table_1.getPlanning(); +``` + +**Version Management**: +```javascript +// Get public version +var publicVersion = planning.getPublicVersion("Budget2024"); + +// Get private version +var privateVersion = planning.getPrivateVersion("myDraft"); + +// Check for unsaved changes +if (publicVersion.isDirty()) { + publicVersion.publish(); +} + +// Copy version +sourceVersion.copy("NewVersion", PlanningCopyOption.AllData, PlanningCategory.Budget); +``` + +**Data Locking**: +```javascript +var selection = Table_1.getSelections()[0]; +var lockState = planning.getDataLocking().getState(selection); +``` + +**Reference**: See `references/api-planning.md` for complete Planning API. + +### 5. Application API + +Global Application object: +```javascript +// User feedback +Application.showBusyIndicator(); +Application.hideBusyIndicator(); + +// Messages +Application.showMessage(ApplicationMessageType.Info, "Operation completed"); + +// Application info +var appInfo = Application.getInfo(); +var userInfo = Application.getUserInfo(); + +// Theme +var theme = Application.getTheme(); +``` + +### 6. Events + +**Application Events**: +- `onInitialization`: Runs once when application loads +- `onPostMessageReceived`: Fires when host app sends PostMessage (iFrame scenarios) +- `onResize`: Fires when application is resized +- `onOrientationChange`: Mobile orientation change + +**Widget Events**: +- `onSelect`: User selects data point (Chart, Table) +- `onResultChanged`: Data in widget changes +- `onClick`: Button click, etc. + +**Best Practice**: Keep `onInitialization` empty for performance. Use static filters instead. + +**iFrame Embedding**: See `references/iframe-embedding-lumira-migration.md` for PostMessage integration. + +--- + +## Common Patterns + +### Filter Data Based on Selection + +```javascript +// onSelect event of Chart_1 +var selections = Chart_1.getSelections(); +if (selections.length > 0) { + var selectedLocation = selections[0]["Location"]; + Table_1.getDataSource().setDimensionFilter("Location", selectedLocation); +} +``` + +### Find Active Version from Attribute + +```javascript +var allVersions = PlanningModel_1.getMembers("Version"); +var activeVersion = ""; + +for (var i = 0; i < allVersions.length; i++) { + if (allVersions[i].properties.Active === "X") { + activeVersion = allVersions[i].id; + break; + } +} +console.log("Active Version: " + activeVersion); +``` + +### Dynamic Measure Swap + +```javascript +// Get current measure +var currentMeasure = Chart_1.getMembers(Feed.ValueAxis); + +// Remove current measure +Chart_1.removeMember(Feed.ValueAxis, currentMeasure[0]); + +// Add new measure +Chart_1.addMember(Feed.ValueAxis, "[Account].[parentId].&[NewMeasure]"); +``` + +### Switch Between Chart and Table + +```javascript +// Show chart, hide table +Chart_1.setVisible(true); +Table_1.setVisible(false); +Button_ShowTable.setVisible(true); +Button_ShowChart.setVisible(false); +``` + +### Pause and Resume Refresh + +```javascript +// Pause before multiple operations +Chart_1.getDataSource().setRefreshPaused(true); +Table_1.getDataSource().setRefreshPaused(true); + +// Apply multiple filters +Chart_1.getDataSource().setDimensionFilter("Year", "2024"); +Chart_1.getDataSource().setDimensionFilter("Region", "EMEA"); + +// Resume refresh (single backend call) +Chart_1.getDataSource().setRefreshPaused(false); +Table_1.getDataSource().setRefreshPaused(false); +``` + +### Get Booked Values Only + +```javascript +var members = Table_1.getDataSource() + .getMembers("Dimension", {accessMode: MemberAccessMode.BookedValues}); +``` + +**Reference**: See `templates/common-patterns.js` for more patterns. + +--- + +## Debugging + +### Console Logging + +```javascript +console.log("Variable value:", myVariable); +console.log("Selections:", Chart_1.getSelections()); +``` + +**Access Console**: +1. Run application +2. Press F12 or Ctrl+Shift+J +3. Go to Console tab +4. Filter by "Info" type +5. Expand "sandbox.worker.main.*.js" for your logs + +### Debugger Statement + +```javascript +debugger; // Execution pauses here +var value = getData(); +``` + +**Enable Debug Mode**: +1. Add `;debug=true` to application URL +2. Open Developer Tools (F12) +3. Go to Sources tab +4. Find code in "sandbox worker main" file + +### Performance Logging + +Add URL parameter: `?APP_PERFORMANCE_LOGGING=true` + +```javascript +// In console +window.sap.raptr.getEntriesByMarker("(Application)") + .filter(e => e.entryType === 'measure') + .sort((a,b) => (a.startTime + a.duration) - (b.startTime + b.duration)); +``` + +### Error Handling + +```javascript +try { + var data = Table_1.getDataSource().getData(selection); + // Process data +} catch (error) { + console.log("Error:", error); + Application.showMessage(ApplicationMessageType.Error, "Operation failed"); +} +``` + +--- + +## Performance Best Practices + +### 1. Minimize Backend Trips + +```javascript +// GOOD: getResultSet() - no backend trip +var resultSet = Chart_1.getDataSource().getResultSet(); + +// AVOID: getMembers() - always hits backend +var members = Chart_1.getDataSource().getMembers("Dimension"); +``` + +### 2. Batch Filter Operations + +```javascript +// GOOD: Pause refresh, apply multiple filters, resume +ds.setRefreshPaused(true); +ds.setDimensionFilter("Dim1", value1); +ds.setDimensionFilter("Dim2", value2); +ds.setDimensionFilter("Dim3", value3); +ds.setRefreshPaused(false); + +// AVOID: Each filter triggers refresh +ds.setDimensionFilter("Dim1", value1); // Refresh +ds.setDimensionFilter("Dim2", value2); // Refresh +ds.setDimensionFilter("Dim3", value3); // Refresh +``` + +### 3. Copy Filters Efficiently + +```javascript +// GOOD: Copy all filters at once +Table_1.getDataSource().copyDimensionFilterFrom(Chart_1.getDataSource()); + +// AVOID: Set each filter individually +var filters = Chart_1.getDataSource().getDimensionFilters("Dim1"); +Table_1.getDataSource().setDimensionFilter("Dim1", filters[0].value); +``` + +### 4. Empty onInitialization + +```javascript +// AVOID: Heavy operations in onInitialization +// This blocks application startup + +// GOOD: Use URL parameters for initial values +// Use static widget filters instead of script filters +// Load invisible widgets in background +``` + +### 5. Cache DataSource References + +```javascript +// GOOD: Cache reference outside loop +var ds = Table_1.getDataSource(); +for (var i = 0; i < items.length; i++) { + ds.setDimensionFilter("Dim", items[i]); +} + +// AVOID: Get reference inside loop +for (var i = 0; i < items.length; i++) { + Table_1.getDataSource().setDimensionFilter("Dim", items[i]); +} +``` + +--- + +## Developer Best Practices + +Maintainable SAC stories require consistent conventions. Key principles: + +### 1. Naming Conventions + +Use prefixes to identify widget types at a glance: + +| Widget Type | Prefix | Example | +|-------------|--------|---------| +| Bar Chart | `chartB_` | `chartB_revenue_by_state` | +| Line Chart | `chartL_` | `chartL_margin_by_product` | +| Table | `tbl_` | `tbl_invoices_by_month` | +| Panel | `pnl_` | `pnl_header` | +| Button | `btn_` | `btn_export_pdf` | +| Dropdown | `ddl_` | `ddl_product` | +| KPI/Numeric | `kpi_` | `kpi_actuals_vs_budget` | + +### 2. Layout Organization + +- Group related widgets in panels +- Order widgets in Outline view: top→bottom, left→right +- Use panel visibility for show/hide sections: + +```javascript +// Hide entire section instead of individual widgets +pnl_details.setVisible(false); +``` + +### 3. Script Annotation + +Add summary + line-level comments to all scripts: + +```javascript +/* + * Script: onSelect - chartB_revenue_by_region + * Purpose: Filter detail table when user selects a region + * Dependencies: chartB_revenue_by_region, tbl_transactions + */ + +// Get user's selection from the chart +var selections = chartB_revenue_by_region.getSelections(); +``` + +### 4. Design Tips + +- **Disable Panel Auto Scroll**: Prevents unwanted scroll bars +- **Avoid Flow Panels**: Fixed layouts are easier to maintain +- **Use % for Left/Right**: Change from px to % after layout is finalized for responsive sizing + +**Full Reference**: See `references/best-practices-developer.md` for complete naming table and patterns. + +**Source**: [SAP Community - 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) + +--- + +## Planning Story Architecture + +For complex planning processes, use multi-story architecture with a central entry point. + +### Key Principles + +1. **Entry Point Design**: Create overview page with KPI tiles and organized navigation sections +2. **Multi-Story Separation**: One story per planning phase, stored in folder structure +3. **Script-Based Navigation**: Use `NavigationUtils.openStory()` instead of hyperlinks +4. **User Assistance**: Consistent sidebar with filters, step-by-step instructions, external links +5. **Guided Process**: "Guide Me!" popup for focused step-by-step workflow +6. **Button Color Coding**: Green (positive), Red (negative), Blue (neutral) + +### Navigation Script Pattern + +```javascript +// Navigate to target story with page and mode parameters +var urlParameters = ArrayUtils.create(Type.UrlParameter); +urlParameters.push(UrlParameter.create("mode", "view")); +urlParameters.push(UrlParameter.create("page", "0")); +NavigationUtils.openStory(storyId, "", urlParameters, false); +``` + +### Folder Organization + +``` +Planning_Application/ +├── 00_Entry_Point.story +├── 01_Configuration.story +├── 02_Plan_FTE.story +├── 03_Plan_Costs.story +└── 04_Reports.story +``` + +**Full Reference**: See `references/best-practices-planning-stories.md` for complete patterns including sidebar design, guided process popup implementation, and button guidelines. + +**Source**: [SAP PRESS - Best Practices for Planning Stories](https://blog.sap-press.com/best-practices-for-planning-stories-in-sap-analytics-cloud) + +--- + +## Popups and Dialogs + +### Create Popup + +1. Outline panel → Popups → (+) +2. Add widgets to popup (tables, charts, buttons) +3. Optionally enable header/footer for dialog style + +### Show/Hide Popup + +```javascript +// Show popup +Popup_1.open(); + +// Hide popup +Popup_1.close(); +``` + +### Busy Indicator + +```javascript +Application.showBusyIndicator(); + +// Perform operations +Table_1.getDataSource().refreshData(); + +Application.hideBusyIndicator(); +``` + +--- + +## Utility Functions + +### Type Conversion + +```javascript +// String to Integer +var num = ConvertUtils.stringToInteger("42"); + +// Number to String +var str = ConvertUtils.numberToString(42); +``` + +### Array Operations + +```javascript +// ArrayUtils available for common operations +var arr = [1, 2, 3]; +``` + +### Date Formatting + +```javascript +// DateFormat utility +var formatted = DateFormat.format(dateValue, "yyyy-MM-dd"); +``` + +--- + +## Script Objects (Reusable Functions) + +Create reusable functions not tied to specific events: + +1. Outline panel → Script Objects → (+) +2. Add functions to script object +3. Call from any event: `ScriptObject_1.myFunction(param);` + +```javascript +// In ScriptObject_1 +function applyStandardFilters(dataSource, year, region) { + dataSource.setDimensionFilter("Year", year); + dataSource.setDimensionFilter("Region", region); +} + +// In event handler +ScriptObject_1.applyStandardFilters(Table_1.getDataSource(), "2024", "EMEA"); +``` + +--- + +## Export Capabilities + +### PDF Export + +```javascript +var exportSettings = { + scope: ExportScope.All, + header: "Report Title", + footer: "Page {page}", + orientation: "landscape" +}; +Application.export(ExportType.PDF, exportSettings); +``` + +### Excel Export + +```javascript +Application.export(ExportType.Excel, { + scope: ExportScope.All, + includeHierarchy: true +}); +``` + +### CSV Export + +```javascript +Table_1.export(ExportType.CSV); +``` + +--- + +## Restrictions & Unsupported (2025.23) +- **Composites**: many editor/runtime gaps; export/bookmark only at composite level; limited linked analysis; no `onInitialization` event (`references/composites-restrictions.md`). +- **Analytics Designer**: popup sizing/theme issues, planning/version API limits, DataSource filter/variable constraints, chart feed/event limits, export quirks, Safari R-vis cookie issue, embedding not supported, input-control constraints (`references/analytics-designer-restrictions.md`). +- **Blending**: primary-model-only API support; several filter/table/planning/comment APIs unsupported; IDs include modelId; value help differences (`references/blending-limitations.md`). +- **Authentication-required page**: one Help Portal article needs SAP login (`references/auth-required.md`). + +--- + +## Official Documentation Links + +### API References (Always Current) +- **Analytics Designer API Reference 2025.23**: https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html +- **Optimized Story Experience API Reference 2025.23**: https://help.sap.com/doc/1639cb9ccaa54b2592224df577abe822/release/en-US/index.html + +### SAP Help Portal +- **Scripting Documentation**: https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/6a4db9a9c8634bcb86cecbf1f1dbbf8e.html +- **Performance Best Practices**: https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/fbe339efda1241b5a3f46cf17f54cdff.html + +### Learning Resources +- **SAP Learning Journey**: https://learning.sap.com/learning-journeys/acquiring-basic-scripting-skills-to-extend-stories-in-sap-analytics-cloud +- **SAP Developer Tutorials**: https://developers.sap.com/tutorials/sac-analytics-designer-create-1-create.html + +### Community Resources +- **SAP Community - SAC**: https://community.sap.com/topics/cloud-analytics +- **Code Snippets**: https://www.denisreis.com/sap-analytics-cloud-javascript-api-code-snippets/ + +--- + +## Bundled Reference Files + +This skill includes detailed reference documentation (52 files): + +**Core APIs**: +1. **references/api-datasource.md**: Complete DataSource API (36+ methods) +2. **references/api-widgets.md**: Chart, Table, Input Controls, GeoMap APIs +3. **references/api-planning.md**: Planning API, version management, data locking +4. **references/api-application.md**: Application object, utilities, events + +**Advanced APIs**: +5. **references/api-calendar-bookmarks.md**: Calendar integration, Bookmarks, Linked Analysis, Timer API +6. **references/api-advanced-widgets.md**: Container widgets, Layout API, R Visualization, Custom Widgets, Navigation +7. **references/api-data-operations.md**: Range/exclude filters, dimension properties, hierarchies, members, DataSource info + +**Scripting Fundamentals**: +8. **references/scripting-language-fundamentals.md**: Type system, variables, control flow, loops, operators, built-in objects, arrays, method chaining, security +9. **references/debugging-browser-tools.md**: Console logging, browser debugging, debug mode, breakpoints, R visualization debugging, performance logging + +**Best Practices**: +10. **references/best-practices-developer.md**: Naming conventions, layout organization, script annotation, responsive design +11. **references/best-practices-planning-stories.md**: Multi-story architecture, entry point design, navigation scripting, user assistance patterns + +**Embedding & Migration**: +12. **references/iframe-embedding-lumira-migration.md**: iFrame PostMessage communication, Lumira Designer migration guidance + +**2025.23 Addenda (Help Portal extracts)**: +13. **references/comments.md** – Comments APIs for widgets/table cells +14. **references/search-to-insight.md** – Search to Insight technical object +15. **references/time-series-forecast.md** – Forecast enablement on charts +16. **references/composites-scripting.md** – Add scripts to composites +17. **references/composites-restrictions.md** – Composite restrictions +18. **references/blending-limitations.md** – Blending unsupported APIs and notes +19. **references/analytics-designer-restrictions.md** – Known restrictions +20. **references/geomap.md** – Geo map quick menus and scripting +21. **references/text-widget.md** – Dynamic text & text widget APIs +22. **references/smart-grouping.md** – Smart grouping APIs +23. **references/explorer-smart-insights.md** – Explorer and Smart Insights +24. **references/auth-required.md** – Auth-required Help Portal page +25. **references/script-editor.md** – Script editor basics +26. **references/value-help.md** – Value help/auto-complete in editor +27. **references/script-variables.md** – Global/script variables & URL params +28. **references/script-objects.md** – Reusable script objects +29. **references/pattern-functions.md** – Pattern-based functions (ML) +30. **references/optimize-type-libraries.md** – Optimize type libraries +31. **references/automatic-refactoring.md** – Automatic refactoring tools +32. **references/debug-scripts.md** – Debug mode & breakpoints +33. **references/check-errors.md** – Errors panel usage +34. **references/check-references.md** – Reference checking +35. **references/script-performance-popup.md** – Script performance analysis +36. **references/input-fields.md** – Input fields/text areas +37. **references/sliders.md** – Sliders and range sliders +38. **references/bind-widget-values.md** – Bind widget values to variables +39. **references/bookmark-settings.md** – Bookmark settings/APIs +40. **references/bookmark-set-tech-object.md** – Bookmark set technical object +41. **references/calendar-integration-tech-object.md** – Calendar integration tech object +42. **references/data-change-insights-tech-object.md** – Data change insights tech object +43. **references/export-pdf-tech-object.md** – Export to PDF tech object +44. **references/export-ppt-tech-object.md** – Export to PowerPoint tech object +45. **references/text-pool-tech-object.md** – Text pool tech object +46. **references/timer-tech-object.md** – Timer tech object +47. **references/planning-model-tech-object.md** – Planning model tech object +48. **references/data-actions-tech-object.md** – Data actions tech object +49. **references/multi-actions-tech-object.md** – Multi actions tech object +50. **references/odata-service.md** – OData service technical object +51. **references/on-after-data-entry-process.md** – Table onAfterDataEntryProcess event +52. **references/memberinfo-performance.md** – MemberInfo performance guidance + +**Templates** (40 ready-to-use patterns): +1. **templates/common-patterns.js**: 40 patterns - filtering, data access, loops, arrays, R visualization, type conversion, etc. +2. **templates/planning-operations.js**: Version management, workflows, data actions + +--- + +## Version Compatibility + +- **Minimum SAC Version**: 2025.23 +- **API Reference Version**: 2025.23 +- **Analytics Designer**: Fully supported +- **Optimized Story Experience**: Fully supported (Advanced Mode) + +**Note**: Analytics Designer is being strategically replaced by Advanced Mode in Optimized Story Experience. Both share similar scripting APIs. + +--- + +## Instructions for Claude + +When assisting with SAC scripting: + +1. **Use correct API syntax**: Reference official API documentation +2. **Prefer getResultSet()**: Over getMembers() for performance +3. **Batch operations**: Use pause/resume refresh pattern +4. **Include error handling**: Use try-catch for robust code +5. **Follow naming conventions**: CamelCase for variables, descriptive names +6. **Test incrementally**: Suggest testing every 5-10 lines +7. **Link to documentation**: Provide relevant SAP Help links +8. **Consider performance**: Avoid heavy operations in onInitialization + +When writing scripts: +- Start with variable declarations +- Use console.log() for debugging +- Cache DataSource references +- Handle empty selections gracefully +- Use Application.showBusyIndicator() for long operations + +For troubleshooting: +- Check browser console (F12) +- Verify widget names in Outline +- Confirm DataSource is connected +- Test with simple console.log first +- Use `;debug=true` URL parameter + +--- + +**License**: MIT +**Version**: 1.6.0 +**Maintained by**: SAP Skills Maintainers +**Repository**: https://github.com/secondsky/sap-skills diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..c51b54e --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,269 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:secondsky/sap-skills:skills/sap-sac-scripting", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "1bbfbdb431686a41c7ba12a8b9216ad4ac02684a", + "treeHash": "b77479b2b547697e4c4dc8773010758843abbc7001077ae06dd5c018bf891fcb", + "generatedAt": "2025-11-28T10:28:14.747430Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "sap-sac-scripting", + "description": "Scripting for SAC Analytics Designer and stories. Covers DataSource API, Chart/Table manipulation, Planning operations, Calendar integration, and 39 code templates.", + "version": "1.1.0" + }, + "content": { + "files": [ + { + "path": "SKILL.md.backup", + "sha256": "547ae1186b8c3038fa7fc75ed35b0c76126d0c41d7f49b64e6e978660288b1c9" + }, + { + "path": "README.md", + "sha256": "2dcf847211cc7593f5b61a9a4962c85a655bd322f4e179bfa2b0987f4371c2aa" + }, + { + "path": "SKILL.md", + "sha256": "d26d8337591e2992cec39c4b9ff14776669da798253ec400cb26674704d2de19" + }, + { + "path": "references/debug-scripts.md", + "sha256": "0010810d27c85585044acdde48af99e2da35b107db232adc7e1ce71acaca608e" + }, + { + "path": "references/whats-new-2025.23.md", + "sha256": "5a21afe34dc35face7993578821f7a25f7784ddc7c7730cb702d48869aa31443" + }, + { + "path": "references/on-after-data-entry-process.md", + "sha256": "7ea449b9d26ef911bc25439c463b28bd18a22437fe044473a9f81429759d39ee" + }, + { + "path": "references/auth-required.md", + "sha256": "bc6cfb9d2b9187eeae7c2126f167e72851a0862a418e83ff38fd4fa3c9c3cc94" + }, + { + "path": "references/value-help.md", + "sha256": "a13e075fd15c22c098d108884f6c60c8f107882255ec8283b961e8e41f0c3f3b" + }, + { + "path": "references/api-calendar-bookmarks.md", + "sha256": "d17574c98187aa2330605ff8a6e3d2ff2a73500857e6a1d730a3edeea7835041" + }, + { + "path": "references/api-planning.md", + "sha256": "990aced69b0008fc6b250dc6685729d1a11dd0046e6f540059b87c7def392341" + }, + { + "path": "references/api-advanced-widgets.md", + "sha256": "2077aa0651d80c60c72c7c1bd4a4b65acb38154c0e74abb492e414bc022fd4e9" + }, + { + "path": "references/script-editor.md", + "sha256": "505b1f3d135291766084ce95f900ce20da4fabbceef1689528bc99a21637fae3" + }, + { + "path": "references/check-errors.md", + "sha256": "e487c98dea7628b733c0b2d3bf15f455b2c228d0d10af6231498d5a9b272af9f" + }, + { + "path": "references/api-widgets.md", + "sha256": "57e8052ea3af6117c92bb83668fbfe257cd5b333873d1e9ce23840f5df7dfd3d" + }, + { + "path": "references/export-pdf-tech-object.md", + "sha256": "3918705dff6ee1f96b6534c2e12a9346daf0611d1fc653c4ce0071fde28d770d" + }, + { + "path": "references/best-practices-developer.md", + "sha256": "76660939b55ba4b817b054b1d54c6ae4d12df80380394a0be420e28ec35f4ca8" + }, + { + "path": "references/timer-tech-object.md", + "sha256": "4f60fcec7ff973673946c75918863b2054d9559c3c09d43d3fe04ae6200f3721" + }, + { + "path": "references/bookmark-settings.md", + "sha256": "3e0b0a4d085c84bdeea9e16c385fd5a3e5544b7f900d288de126f221428071c6" + }, + { + "path": "references/api-datasource.md", + "sha256": "d5b8762df1a21ced35c839cc49f368ccb79d61fcd8ce9827e23af4a2a1f4626b" + }, + { + "path": "references/time-series-forecast.md", + "sha256": "caa875abc2916eff0770dece47812a8e7b020aecb2434b5e529641f19a330ef8" + }, + { + "path": "references/analytics-designer-restrictions.md", + "sha256": "6667eb372ed56274e2dad386dceb67df7a3a965d7f715d230f61e35874bbfb52" + }, + { + "path": "references/explorer-smart-insights.md", + "sha256": "56e1307eb1fcaac0bb13cd819b6ccc5284591aeaf8f2fa3ec444899f3f4b98f6" + }, + { + "path": "references/composites-scripting.md", + "sha256": "90a72b0bb44bb097ae5465b4af3b01b7952c0586dca2ce137de55fc4d6d45265" + }, + { + "path": "references/comments.md", + "sha256": "dfefa8cdd0c00d70e2aeba5473e85082a7e0ff22a6a7bef98fc28fb592d76508" + }, + { + "path": "references/data-actions-tech-object.md", + "sha256": "a2d0ab94ac79914e066e9309e7b7a56c2c37dcce52c19bccf32cde93b5086264" + }, + { + "path": "references/best-practices-planning-stories.md", + "sha256": "90908818a83ce5dcee109d8b2377acb536a7671bfa82b0b9a5dd512daec48a3b" + }, + { + "path": "references/text-pool-tech-object.md", + "sha256": "369a8e18369b80e60c5fb786b67d22922abd2f52195f0f93d73d66b60043b784" + }, + { + "path": "references/debugging-browser-tools.md", + "sha256": "9d5fd4e1102b3cf87b64fd322d0c6236ff9dc0ffdfec56aca06c9e9e3db2d044" + }, + { + "path": "references/export-ppt-tech-object.md", + "sha256": "82c4cf0ca3c4a9106237b7c62fdd52f417f9534a78aa5246e022c280525576c1" + }, + { + "path": "references/script-objects.md", + "sha256": "5ca12ef7f5d5773407fa8c17d7e81a9d16625c67ba0466b007cc4d45023fc9c4" + }, + { + "path": "references/script-variables.md", + "sha256": "58f4da2bb61fe177affa8508a46908aaff97be4dca7adb38decf9bc34b4d50f1" + }, + { + "path": "references/geomap.md", + "sha256": "d71c67dc9bbd200017288129b3d95fe80b63cd99c7f9e73c0542d40a51f85d82" + }, + { + "path": "references/bind-widget-values.md", + "sha256": "f16d5503c1f3d30227e86a1c3350338650a56da42d239e155cd772d83ad6aacc" + }, + { + "path": "references/calendar-integration-tech-object.md", + "sha256": "8f1c0335f71ed95605e726250fc6c1faec05485fd18839407fc8aa0d1dcde30f" + }, + { + "path": "references/multi-actions-tech-object.md", + "sha256": "d8fe2b8ad0ea68f02506b51c134cb4d23507ba46cf95e2ee09abcd929c8bbb9a" + }, + { + "path": "references/input-fields.md", + "sha256": "58994c479d2b3f30ce4a5349d2f7bb84ae2f976e6032d8f12f897a1a83360fe0" + }, + { + "path": "references/memberinfo-performance.md", + "sha256": "b33fc8b80b551fea4301a6782f05b90a5503c6a8225c89005f6de7b5a6bca3b1" + }, + { + "path": "references/scripting-language-fundamentals.md", + "sha256": "5eb5cae858c366a98fd1ba76991f967f4c629c46999e9d39846ec8dcf709c153" + }, + { + "path": "references/script-performance-popup.md", + "sha256": "6e05ee402f9f6d7254d375138f18852192fba913695f979759c2066139d0dd8d" + }, + { + "path": "references/smart-grouping.md", + "sha256": "f89c599c8791df0b6ed2d238e59f0be2d25715b008925f161ba2d9fc0d77c63f" + }, + { + "path": "references/check-references.md", + "sha256": "4a9fba587f08e71f74c881c8ce2ec22cd243ffe58b6341d0026087b68dbee4f8" + }, + { + "path": "references/odata-service.md", + "sha256": "19fa4a81a93e8f7d93166ee41095d2c9c1debf5b64fc542b2efc0bca6f9b0723" + }, + { + "path": "references/pattern-functions.md", + "sha256": "9447795a591b7eba9ea2d5b87c3f59e232de6c5121cf0a1c746b346d1539a7ba" + }, + { + "path": "references/blending-limitations.md", + "sha256": "903015983ae955f9de2b4e837b9046de13fd30a4f8ee8b39cba91fc9e17937dd" + }, + { + "path": "references/iframe-embedding-lumira-migration.md", + "sha256": "48909291129e096afb9eebf1b28a71c9e27ac4206a78d450e55a373f6eca2782" + }, + { + "path": "references/search-to-insight.md", + "sha256": "c304ee533f96517a57d7d19f2d2e71557565ee8331f6939b7f269b8151326e3a" + }, + { + "path": "references/text-widget.md", + "sha256": "b082e3da444d221749742acd270195189e00357f72b2fd3235554656cdf9f250" + }, + { + "path": "references/sliders.md", + "sha256": "68fd8b32037d733d17ce354831d2f8584517e54dae97c7dfe69f7b900d7a9f04" + }, + { + "path": "references/optimize-type-libraries.md", + "sha256": "919523edc20416f1bf1bdfc22b74b8dfeb2c90f213ef9a559b6a3831097b77f5" + }, + { + "path": "references/composites-restrictions.md", + "sha256": "64ca5585d7c8f9ae35dd72cbe493253fe049c5ef93189675a1a43c230d70c5d4" + }, + { + "path": "references/api-application.md", + "sha256": "218966e292962e0132784fb7c9377561d0220312f048851fe56996c4c2cec2ea" + }, + { + "path": "references/planning-model-tech-object.md", + "sha256": "8f76a9778c943c44d40055967ea2b7fcaafdb06e3f141015dbebb0bb1ba82502" + }, + { + "path": "references/data-change-insights-tech-object.md", + "sha256": "151f1a8ce734f63c3272f04fc29afa4cc3b22c1788515e390ddf52a4190b1c81" + }, + { + "path": "references/bookmark-set-tech-object.md", + "sha256": "ada614792fa385586cd7cb68a351484dd0317d795b408a5e33cb40f338530b91" + }, + { + "path": "references/api-data-operations.md", + "sha256": "45d1f1f8d1ca3792ea5a03066583e7018b2775d62bbb4a3455c2b585aa9ba086" + }, + { + "path": "references/automatic-refactoring.md", + "sha256": "7b3d6c51ec6d52d691f516e99c940296f120e1f01deea3d0ae54d0a694d7b4d3" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "95f723e8587fe25271b158e42ed473dd622bf20e89e28b90005c67514c6e0c8b" + }, + { + "path": "templates/common-patterns.js", + "sha256": "156cb76b683b9ff3afb4936d2f645f4e2c9a1a425eeaa2cb49d9f34eddd05d75" + }, + { + "path": "templates/planning-operations.js", + "sha256": "f28248ac3c5892daeda6ca3b04265d86a54c6a23b1784dbb6fa639648b684a25" + } + ], + "dirSha256": "b77479b2b547697e4c4dc8773010758843abbc7001077ae06dd5c018bf891fcb" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/references/analytics-designer-restrictions.md b/references/analytics-designer-restrictions.md new file mode 100644 index 0000000..64ca966 --- /dev/null +++ b/references/analytics-designer-restrictions.md @@ -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. + diff --git a/references/api-advanced-widgets.md b/references/api-advanced-widgets.md new file mode 100644 index 0000000..cb06977 --- /dev/null +++ b/references/api-advanced-widgets.md @@ -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 = ` + +
+
+
+
+ `; + + 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) diff --git a/references/api-application.md b/references/api-application.md new file mode 100644 index 0000000..942cd06 --- /dev/null +++ b/references/api-application.md @@ -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) diff --git a/references/api-calendar-bookmarks.md b/references/api-calendar-bookmarks.md new file mode 100644 index 0000000..51ffd42 --- /dev/null +++ b/references/api-calendar-bookmarks.md @@ -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) diff --git a/references/api-data-operations.md b/references/api-data-operations.md new file mode 100644 index 0000000..c2776a5 --- /dev/null +++ b/references/api-data-operations.md @@ -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 diff --git a/references/api-datasource.md b/references/api-datasource.md new file mode 100644 index 0000000..d7e6cae --- /dev/null +++ b/references/api-datasource.md @@ -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) diff --git a/references/api-planning.md b/references/api-planning.md new file mode 100644 index 0000000..8149eed --- /dev/null +++ b/references/api-planning.md @@ -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) diff --git a/references/api-widgets.md b/references/api-widgets.md new file mode 100644 index 0000000..bb3c83d --- /dev/null +++ b/references/api-widgets.md @@ -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) diff --git a/references/auth-required.md b/references/auth-required.md new file mode 100644 index 0000000..c542e42 --- /dev/null +++ b/references/auth-required.md @@ -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. + diff --git a/references/automatic-refactoring.md b/references/automatic-refactoring.md new file mode 100644 index 0000000..f8c92f2 --- /dev/null +++ b/references/automatic-refactoring.md @@ -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. + diff --git a/references/best-practices-developer.md b/references/best-practices-developer.md new file mode 100644 index 0000000..0b3f72e --- /dev/null +++ b/references/best-practices-developer.md @@ -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) diff --git a/references/best-practices-planning-stories.md b/references/best-practices-planning-stories.md new file mode 100644 index 0000000..28b7747 --- /dev/null +++ b/references/best-practices-planning-stories.md @@ -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) diff --git a/references/bind-widget-values.md b/references/bind-widget-values.md new file mode 100644 index 0000000..b1c11e4 --- /dev/null +++ b/references/bind-widget-values.md @@ -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. + diff --git a/references/blending-limitations.md b/references/blending-limitations.md new file mode 100644 index 0000000..15949b9 --- /dev/null +++ b/references/blending-limitations.md @@ -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. + diff --git a/references/bookmark-set-tech-object.md b/references/bookmark-set-tech-object.md new file mode 100644 index 0000000..43b9f27 --- /dev/null +++ b/references/bookmark-set-tech-object.md @@ -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. + diff --git a/references/bookmark-settings.md b/references/bookmark-settings.md new file mode 100644 index 0000000..abaa88e --- /dev/null +++ b/references/bookmark-settings.md @@ -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. + diff --git a/references/calendar-integration-tech-object.md b/references/calendar-integration-tech-object.md new file mode 100644 index 0000000..6dd8eca --- /dev/null +++ b/references/calendar-integration-tech-object.md @@ -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. + diff --git a/references/check-errors.md b/references/check-errors.md new file mode 100644 index 0000000..2b4d3ab --- /dev/null +++ b/references/check-errors.md @@ -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. + diff --git a/references/check-references.md b/references/check-references.md new file mode 100644 index 0000000..72e8229 --- /dev/null +++ b/references/check-references.md @@ -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`). + diff --git a/references/comments.md b/references/comments.md new file mode 100644 index 0000000..a832f7f --- /dev/null +++ b/references/comments.md @@ -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. + diff --git a/references/composites-restrictions.md b/references/composites-restrictions.md new file mode 100644 index 0000000..b096633 --- /dev/null +++ b/references/composites-restrictions.md @@ -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. + diff --git a/references/composites-scripting.md b/references/composites-scripting.md new file mode 100644 index 0000000..3b8e7c2 --- /dev/null +++ b/references/composites-scripting.md @@ -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. + diff --git a/references/data-actions-tech-object.md b/references/data-actions-tech-object.md new file mode 100644 index 0000000..4ddcadd --- /dev/null +++ b/references/data-actions-tech-object.md @@ -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 + diff --git a/references/data-change-insights-tech-object.md b/references/data-change-insights-tech-object.md new file mode 100644 index 0000000..979ad7b --- /dev/null +++ b/references/data-change-insights-tech-object.md @@ -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. + diff --git a/references/debug-scripts.md b/references/debug-scripts.md new file mode 100644 index 0000000..94a91c2 --- /dev/null +++ b/references/debug-scripts.md @@ -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 `..js` inside folder `` 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:///.../application//?mode=present&debug=true`](https:///.../application//?mode=present&debug=true`) +- Comments are preserved in transformed JS to aid identification. + diff --git a/references/debugging-browser-tools.md b/references/debugging-browser-tools.md new file mode 100644 index 0000000..01045f5 --- /dev/null +++ b/references/debugging-browser-tools.md @@ -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**: `` +- **Script**: `..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 diff --git a/references/explorer-smart-insights.md b/references/explorer-smart-insights.md new file mode 100644 index 0000000..837536a --- /dev/null +++ b/references/explorer-smart-insights.md @@ -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. + diff --git a/references/export-pdf-tech-object.md b/references/export-pdf-tech-object.md new file mode 100644 index 0000000..bc268b4 --- /dev/null +++ b/references/export-pdf-tech-object.md @@ -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`). + diff --git a/references/export-ppt-tech-object.md b/references/export-ppt-tech-object.md new file mode 100644 index 0000000..1880c50 --- /dev/null +++ b/references/export-ppt-tech-object.md @@ -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. + diff --git a/references/geomap.md b/references/geomap.md new file mode 100644 index 0000000..c068ada --- /dev/null +++ b/references/geomap.md @@ -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. + diff --git a/references/iframe-embedding-lumira-migration.md b/references/iframe-embedding-lumira-migration.md new file mode 100644 index 0000000..ac6fea7 --- /dev/null +++ b/references/iframe-embedding-lumira-migration.md @@ -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 diff --git a/references/input-fields.md b/references/input-fields.md new file mode 100644 index 0000000..7e68aa7 --- /dev/null +++ b/references/input-fields.md @@ -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. + diff --git a/references/memberinfo-performance.md b/references/memberinfo-performance.md new file mode 100644 index 0000000..2ac5585 --- /dev/null +++ b/references/memberinfo-performance.md @@ -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. + diff --git a/references/multi-actions-tech-object.md b/references/multi-actions-tech-object.md new file mode 100644 index 0000000..5e80073 --- /dev/null +++ b/references/multi-actions-tech-object.md @@ -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. + diff --git a/references/odata-service.md b/references/odata-service.md new file mode 100644 index 0000000..63b6709 --- /dev/null +++ b/references/odata-service.md @@ -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. + diff --git a/references/on-after-data-entry-process.md b/references/on-after-data-entry-process.md new file mode 100644 index 0000000..01b06dc --- /dev/null +++ b/references/on-after-data-entry-process.md @@ -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. + diff --git a/references/optimize-type-libraries.md b/references/optimize-type-libraries.md new file mode 100644 index 0000000..60143ce --- /dev/null +++ b/references/optimize-type-libraries.md @@ -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. + diff --git a/references/pattern-functions.md b/references/pattern-functions.md new file mode 100644 index 0000000..874a967 --- /dev/null +++ b/references/pattern-functions.md @@ -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. + diff --git a/references/planning-model-tech-object.md b/references/planning-model-tech-object.md new file mode 100644 index 0000000..c8b5eb9 --- /dev/null +++ b/references/planning-model-tech-object.md @@ -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. + diff --git a/references/script-editor.md b/references/script-editor.md new file mode 100644 index 0000000..9ebe971 --- /dev/null +++ b/references/script-editor.md @@ -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 ` - `. + +## Authoring pattern +`.();` + +## 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). + diff --git a/references/script-objects.md b/references/script-objects.md new file mode 100644 index 0000000..88bf9c8 --- /dev/null +++ b/references/script-objects.md @@ -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. + diff --git a/references/script-performance-popup.md b/references/script-performance-popup.md new file mode 100644 index 0000000..0ace01c --- /dev/null +++ b/references/script-performance-popup.md @@ -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. + diff --git a/references/script-variables.md b/references/script-variables.md new file mode 100644 index 0000000..8508103 --- /dev/null +++ b/references/script-variables.md @@ -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. + diff --git a/references/scripting-language-fundamentals.md b/references/scripting-language-fundamentals.md new file mode 100644 index 0000000..27a6240 --- /dev/null +++ b/references/scripting-language-fundamentals.md @@ -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 diff --git a/references/search-to-insight.md b/references/search-to-insight.md new file mode 100644 index 0000000..78258b5 --- /dev/null +++ b/references/search-to-insight.md @@ -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. + diff --git a/references/sliders.md b/references/sliders.md new file mode 100644 index 0000000..9bcde22 --- /dev/null +++ b/references/sliders.md @@ -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. + diff --git a/references/smart-grouping.md b/references/smart-grouping.md new file mode 100644 index 0000000..3abb2c2 --- /dev/null +++ b/references/smart-grouping.md @@ -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. + diff --git a/references/text-pool-tech-object.md b/references/text-pool-tech-object.md new file mode 100644 index 0000000..0b71e58 --- /dev/null +++ b/references/text-pool-tech-object.md @@ -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. + diff --git a/references/text-widget.md b/references/text-widget.md new file mode 100644 index 0000000..8cb63b5 --- /dev/null +++ b/references/text-widget.md @@ -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. + diff --git a/references/time-series-forecast.md b/references/time-series-forecast.md new file mode 100644 index 0000000..eec1420 --- /dev/null +++ b/references/time-series-forecast.md @@ -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. + diff --git a/references/timer-tech-object.md b/references/timer-tech-object.md new file mode 100644 index 0000000..0653616 --- /dev/null +++ b/references/timer-tech-object.md @@ -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. + diff --git a/references/value-help.md b/references/value-help.md new file mode 100644 index 0000000..34c445a --- /dev/null +++ b/references/value-help.md @@ -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. + diff --git a/references/whats-new-2025.23.md b/references/whats-new-2025.23.md new file mode 100644 index 0000000..553421b --- /dev/null +++ b/references/whats-new-2025.23.md @@ -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 diff --git a/templates/common-patterns.js b/templates/common-patterns.js new file mode 100644 index 0000000..1c67ff1 --- /dev/null +++ b/templates/common-patterns.js @@ -0,0 +1,831 @@ +/** + * SAP Analytics Cloud - Common Scripting Patterns + * + * Ready-to-use code patterns for SAC Analytics Designer and Optimized Story Experience. + * Copy and adapt these patterns to your application. + * + * Source: SAP Analytics Cloud Scripting Skill + * Version: 2025.14+ + */ + +// ============================================================================= +// FILTERING PATTERNS +// ============================================================================= + +/** + * Pattern 1: Apply filter from chart selection to table + * Use in: Chart.onSelect event + */ +function filterTableFromChartSelection() { + var selections = Chart_1.getSelections(); + if (selections.length > 0) { + var selectedMember = selections[0]["{{DimensionId}}"]; + Table_1.getDataSource().setDimensionFilter("{{DimensionId}}", selectedMember); + } +} + +/** + * Pattern 2: Apply multiple filters efficiently (batch) + * Use when: Setting multiple filters at once + */ +function applyMultipleFilters(year, region, product) { + var ds = Chart_1.getDataSource(); + + // Pause to prevent multiple refreshes + ds.setRefreshPaused(true); + + ds.setDimensionFilter("Year", year); + ds.setDimensionFilter("Region", region); + ds.setDimensionFilter("Product", product); + + // Single refresh + ds.setRefreshPaused(false); +} + +/** + * Pattern 3: Sync filters across multiple widgets + * Use when: Multiple charts/tables should have same filters + */ +function syncFiltersAcrossWidgets() { + var sourceDs = Chart_1.getDataSource(); + + // Copy all filters efficiently + Table_1.getDataSource().copyDimensionFilterFrom(sourceDs); + Chart_2.getDataSource().copyDimensionFilterFrom(sourceDs); +} + +/** + * Pattern 4: Reset all filters + * Use in: Reset button onClick event + */ +function resetAllFilters() { + var widgets = [Chart_1, Table_1, Chart_2]; + + widgets.forEach(function(widget) { + widget.getDataSource().clearAllFilters(); + }); + + // Reset dropdowns + Dropdown_Year.setSelectedKey(""); + Dropdown_Region.setSelectedKey(""); +} + +/** + * Pattern 5: Filter from dropdown selection + * Use in: Dropdown.onSelect event + */ +function filterFromDropdown() { + var selectedYear = Dropdown_Year.getSelectedKey(); + + if (selectedYear) { + Application.showBusyIndicator(); + + Chart_1.getDataSource().setDimensionFilter("Year", selectedYear); + Table_1.getDataSource().setDimensionFilter("Year", selectedYear); + + Application.hideBusyIndicator(); + } +} + + +// ============================================================================= +// DATA ACCESS PATTERNS +// ============================================================================= + +/** + * Pattern 6: Find active version from attribute + * Use when: Need to identify active planning cycle/version from master data + */ +function findActiveVersion() { + var allVersions = PlanningModel_1.getMembers("Version"); + var activeVersion = null; + + for (var i = 0; i < allVersions.length; i++) { + if (allVersions[i].properties.Active === "X") { + activeVersion = allVersions[i].id; + break; + } + } + + console.log("Active Version: " + activeVersion); + return activeVersion; +} + +/** + * Pattern 7: Get booked values only (not all master data) + * Use when: Need only members that have data, not entire master data list + */ +function getBookedValuesOnly() { + var bookedMembers = Table_1.getDataSource().getMembers( + "{{DimensionId}}", + { accessMode: MemberAccessMode.BookedValues } + ); + + console.log("Booked members:", bookedMembers); + return bookedMembers; +} + +/** + * Pattern 8: Get data value for specific selection + * Use when: Need to read specific cell value + */ +function getSpecificDataValue() { + var selection = { + "@MeasureDimension": "[Account].[parentId].&[Revenue]", + "Location": "[Location].[Country].&[US]", + "Year": "[Date].[Year].&[2024]" + }; + + var data = Chart_1.getDataSource().getData(selection); + + console.log("Formatted:", data.formattedValue); + console.log("Raw:", data.rawValue); + + return data; +} + + +// ============================================================================= +// CHART MANIPULATION PATTERNS +// ============================================================================= + +/** + * Pattern 9: Dynamic measure swap + * Use in: Button onClick or Dropdown onSelect + */ +function swapMeasure(newMeasureId) { + // Get current measure + var currentMeasures = Chart_1.getMembers(Feed.ValueAxis); + + // Remove current measure + if (currentMeasures.length > 0) { + Chart_1.removeMember(Feed.ValueAxis, currentMeasures[0]); + } + + // Add new measure + Chart_1.addMember(Feed.ValueAxis, newMeasureId); +} + +/** + * Pattern 10: Dynamic dimension swap + * Use when: Changing chart axis dimension + */ +function swapDimension(newDimensionId) { + // Get current dimension + var currentDimensions = Chart_1.getMembers(Feed.CategoryAxis); + + // Remove current + if (currentDimensions.length > 0) { + Chart_1.removeDimension(Feed.CategoryAxis, currentDimensions[0]); + } + + // Add new + Chart_1.addDimension(Feed.CategoryAxis, newDimensionId); +} + +/** + * Pattern 11: Top N ranking + * Use when: Show only top performers + */ +function showTopN(n, measureId) { + Chart_1.rankBy(measureId, n, SortOrder.Descending); +} + + +// ============================================================================= +// VISIBILITY TOGGLE PATTERNS +// ============================================================================= + +/** + * Pattern 12: Switch between chart and table + * Use in: Toggle button onClick event + */ +function switchChartTable(showChart) { + Chart_1.setVisible(showChart); + Table_1.setVisible(!showChart); + + Button_ShowChart.setVisible(!showChart); + Button_ShowTable.setVisible(showChart); +} + +/** + * Pattern 13: Expandable panel pattern + * Use when: Toggle detail section visibility + */ +function toggleDetailPanel() { + var isVisible = Panel_Details.isVisible(); + Panel_Details.setVisible(!isVisible); + + // Update button text + if (isVisible) { + Button_Toggle.setText("Show Details"); + } else { + Button_Toggle.setText("Hide Details"); + } +} + + +// ============================================================================= +// USER FEEDBACK PATTERNS +// ============================================================================= + +/** + * Pattern 14: Long operation with busy indicator + * Use when: Performing operations that take time + */ +function performLongOperation() { + Application.showBusyIndicator(); + + try { + // Perform operation + Table_1.getDataSource().refreshData(); + Chart_1.getDataSource().refreshData(); + + Application.showMessage( + ApplicationMessageType.Success, + "Data refreshed successfully" + ); + } catch (error) { + console.log("Error:", error); + Application.showMessage( + ApplicationMessageType.Error, + "Failed to refresh data" + ); + } finally { + Application.hideBusyIndicator(); + } +} + +/** + * Pattern 15: Confirmation popup pattern + * Use when: Confirming destructive actions + */ +// In main button onClick: +function showConfirmation() { + Popup_Confirm.open(); +} + +// In popup Yes button onClick: +function onConfirmYes() { + Popup_Confirm.close(); + performAction(); +} + +// In popup No button onClick: +function onConfirmNo() { + Popup_Confirm.close(); +} + + +// ============================================================================= +// SELECTION HANDLING PATTERNS +// ============================================================================= + +/** + * Pattern 16: Handle multi-selection from table + * Use in: Table.onSelect event + */ +function handleMultiSelection() { + var selections = Table_1.getSelections(); + + if (selections.length === 0) { + Application.showMessage( + ApplicationMessageType.Warning, + "Please select at least one row" + ); + return; + } + + // Process all selections + var selectedIds = selections.map(function(sel) { + return sel["{{DimensionId}}"]; + }); + + console.log("Selected IDs:", selectedIds); + + // Apply as filter (if single value needed) + if (selections.length === 1) { + Chart_1.getDataSource().setDimensionFilter( + "{{DimensionId}}", + selectedIds[0] + ); + } +} + +/** + * Pattern 17: Clear selection + * Use when: Need to programmatically clear widget selection + */ +function clearSelection() { + Chart_1.getDataSource().removeDimensionFilter("{{DimensionId}}"); + Table_1.getDataSource().removeDimensionFilter("{{DimensionId}}"); +} + + +// ============================================================================= +// INITIALIZATION PATTERNS +// ============================================================================= + +/** + * Pattern 18: Set initial filters from URL parameters + * Prerequisites: + * - Create global variable with p_ prefix (e.g., p_year) + * - URL: ?p_year=2024&p_region=EMEA + */ +// In Script Object (not onInitialization): +function applyUrlParameters() { + if (p_year) { + Chart_1.getDataSource().setDimensionFilter("Year", p_year); + Dropdown_Year.setSelectedKey(p_year); + } + + if (p_region) { + Chart_1.getDataSource().setDimensionFilter("Region", p_region); + Dropdown_Region.setSelectedKey(p_region); + } +} + +/** + * Pattern 19: Dynamic title update + * Use when: Title should reflect current filters + */ +function updateDynamicTitle() { + var year = Dropdown_Year.getSelectedKey() || "All Years"; + var region = Dropdown_Region.getSelectedKey() || "All Regions"; + + Text_Title.setText("Sales Report - " + year + " | " + region); +} + + +// ============================================================================= +// DEBUGGING PATTERNS +// ============================================================================= + +/** + * Pattern 20: Debug logging helper + * Use during development + */ +function debugLog(label, value) { + console.log("=== " + label + " ==="); + console.log(value); + console.log("Type:", typeof value); + if (Array.isArray(value)) { + console.log("Length:", value.length); + } +} + +/** + * Pattern 21: Log all selections + * Use when debugging selection handling + */ +function logSelections(widgetName, widget) { + var selections = widget.getSelections(); + console.log(widgetName + " selections:"); + console.log(JSON.stringify(selections)); +} + + +// ============================================================================= +// EXPORT PATTERNS +// ============================================================================= + +/** + * Pattern 22: Export with dynamic filename + * Use in: Export button onClick + */ +function exportWithDynamicName() { + var today = new Date().toISOString().split('T')[0]; + var year = Dropdown_Year.getSelectedKey() || "AllYears"; + + Table_1.export(ExportType.Excel, { + fileName: "SalesReport_" + year + "_" + today + }); +} + +/** + * Pattern 23: PDF export with header/footer + * Use in: Export PDF button onClick + */ +function exportPDF() { + var year = Dropdown_Year.getSelectedKey() || "All Years"; + var today = new Date().toLocaleDateString(); + + Application.export(ExportType.PDF, { + scope: ExportScope.All, + header: "Sales Analysis Report - " + year, + footer: "Generated: " + today + " | Page {page}", + orientation: "landscape" + }); +} + + +// ============================================================================= +// TYPE CONVERSION PATTERNS (SAC requires explicit conversion) +// ============================================================================= + +/** + * Pattern 24: Explicit type conversion (required in SAC) + * Use when: Concatenating strings with numbers + */ +function typeConversionExamples() { + var myNumber = 42; + var myDecimal = 3.14; + var myBoolean = true; + + // WRONG: Auto-conversion not allowed + // console.log("Value: " + myNumber); + + // CORRECT: Explicit conversion + console.log("Integer: " + myNumber.toString()); + console.log("Decimal: " + myDecimal.toString()); + console.log("Boolean: " + myBoolean.toString()); + + // Using ConvertUtils + var numStr = ConvertUtils.numberToString(myNumber); + var strNum = ConvertUtils.stringToInteger("42"); +} + +/** + * Pattern 25: String formatting utilities + * Use when: Formatting data for display + */ +function stringFormatting() { + var value = 1234.567; + + // Fixed decimal places + var fixed = value.toFixed(2); // "1234.57" + + // Format number (use NumberFormat) + var formatted = NumberFormat.format(value, "#,##0.00"); + + // Date formatting + var now = new Date(); + var dateStr = DateFormat.format(now, "yyyy-MM-dd"); + var timeStr = DateFormat.format(now, "HH:mm:ss"); +} + + +// ============================================================================= +// LOOP PATTERNS (SAC-specific requirements) +// ============================================================================= + +/** + * Pattern 26: For loop with explicit iterator declaration + * IMPORTANT: SAC requires 'var' declaration for loop iterator + */ +function forLoopPattern() { + var members = Table_1.getDataSource().getMembers("Location", 10); + + // CORRECT: Iterator declared with var + for (var i = 0; i < members.length; i++) { + console.log(members[i].id + ": " + members[i].description); + } + + // WRONG: Iterator not declared (will cause error) + // for (i = 0; i < members.length; i++) { ... } +} + +/** + * Pattern 27: While loop pattern + * Use when: Iterating until condition met + */ +function whileLoopPattern() { + var count = 0; + var maxIterations = 10; + + while (count < maxIterations) { + console.log("Iteration: " + count.toString()); + count++; + + // Safety break condition + if (count > 100) { + console.log("Safety break triggered"); + break; + } + } +} + +/** + * Pattern 28: For-in loop for selections + * Use when: Iterating over selection object properties + */ +function forInLoopPattern() { + var selection = Table_1.getSelections()[0]; + + if (selection) { + console.log("Selection properties:"); + for (var propKey in selection) { + var propValue = selection[propKey]; + console.log(" " + propKey + ": " + propValue); + } + } +} + + +// ============================================================================= +// ARRAY PATTERNS +// ============================================================================= + +/** + * Pattern 29: Create typed 1D array + * Use when: Creating arrays for API calls + */ +function create1DArray() { + // Using ArrayUtils for typed arrays + var numberArray = ArrayUtils.create(Type.number); + numberArray.push(1); + numberArray.push(2); + numberArray.push(3); + + var stringArray = ArrayUtils.create(Type.string); + stringArray.push("A"); + stringArray.push("B"); + + // Literal syntax also works + var simpleArray = [1, 2, 3]; +} + +/** + * Pattern 30: Create 2D array (matrix) + * Use when: Building tabular data structures + */ +function create2DArray() { + var numCols = 4; + var numRows = 3; + + // Create first row (required for type inference) + var firstRow = ArrayUtils.create(Type.number); + var arr2D = [firstRow]; + + // Add remaining rows + for (var i = 1; i < numRows; i++) { + arr2D.push(ArrayUtils.create(Type.number)); + } + + // Fill with values + var count = 0; + for (var row = 0; row < numRows; row++) { + for (var col = 0; col < numCols; col++) { + arr2D[row][col] = count; + count++; + } + } + + // Access values + var value = arr2D[1][2]; // Row 1, Column 2 + console.log("Value at [1][2]: " + value.toString()); +} + + +// ============================================================================= +// ADVANCED FILTER PATTERNS +// ============================================================================= + +/** + * Pattern 31: Exclude filter (filter OUT specific values) + * Use when: Removing specific members from drill-down + */ +function excludeFilterPattern() { + // Exclude single value + DS_1.setDimensionFilter("EMPLOYEE_ID", { + value: "230", + exclude: true + }); + + // Exclude multiple values + DS_1.setDimensionFilter("EMPLOYEE_ID", { + values: ["230", "240", "250"], + exclude: true + }); +} + +/** + * Pattern 32: Range filter (numeric dimensions only) + * Use when: Filtering by numeric ranges + * NOTE: Not supported for time dimensions or SAP BW + */ +function rangeFilterPattern() { + // Between range + DS_1.setDimensionFilter("EMPLOYEE_ID", { + from: "100", + to: "500" + }); + + // Less than + DS_1.setDimensionFilter("EMPLOYEE_ID", { + less: "100" + }); + + // Greater than or equal + DS_1.setDimensionFilter("EMPLOYEE_ID", { + greaterOrEqual: "500" + }); + + // Multiple ranges (OR logic) + DS_1.setDimensionFilter("EMPLOYEE_ID", [ + { less: "100" }, + { greater: "900" } + ]); +} + +/** + * Pattern 33: Get and process dimension filters + * Use when: Need to read current filter state + */ +function getFilterState() { + var filters = Table_1.getDataSource().getDimensionFilters("COUNTRY"); + + for (var i = 0; i < filters.length; i++) { + var filter = filters[i]; + + switch (filter.type) { + case FilterValueType.Single: + var single = cast(Type.SingleFilterValue, filter); + console.log("Single filter: " + single.value); + break; + + case FilterValueType.Multiple: + var multi = cast(Type.MultipleFilterValue, filter); + console.log("Multiple filter: " + multi.values.join(", ")); + break; + + case FilterValueType.Range: + var range = cast(Type.RangeFilterValue, filter); + if (range.from) { + console.log("Range: " + range.from + " to " + range.to); + } + break; + } + } +} + + +// ============================================================================= +// HIERARCHY PATTERNS +// ============================================================================= + +/** + * Pattern 34: Set hierarchy drill level + * Use when: Controlling hierarchy expansion + */ +function setHierarchyLevel() { + var ds = Table_1.getDataSource(); + + // Set active hierarchy + ds.setHierarchy("Location", "State_Hierarchy"); + + // Set drill level (0 = collapsed, higher = more expanded) + ds.setHierarchyLevel("Location", 2); + + // Get current level + var currentLevel = ds.getHierarchyLevel("Location"); + console.log("Current level: " + currentLevel.toString()); +} + +/** + * Pattern 35: Expand/collapse hierarchy nodes + * Use when: Programmatic hierarchy manipulation + */ +function expandCollapseNode() { + var ds = Chart_1.getDataSource(); + + // Expand specific node + ds.expandNode("Location", { + "Location": "[Location].[State_Hierarchy].&[California]", + "@MeasureDimension": "[Account].[parentId].&[Revenue]" + }); + + // Collapse node + ds.collapseNode("Location", { + "Location": "[Location].[State_Hierarchy].&[California]", + "@MeasureDimension": "[Account].[parentId].&[Revenue]" + }); +} + + +// ============================================================================= +// R VISUALIZATION PATTERNS +// ============================================================================= + +/** + * Pattern 36: Read R environment variable from JavaScript + * Use when: Getting calculated values from R script + */ +function readFromREnvironment() { + // R script created: gmCorrelation <- cor(grossMargin, grossMarginPlan) + + var correlation = RVisualization_1 + .getEnvironmentValues() + .getNumber("gmCorrelation"); + + console.log("Correlation coefficient: " + correlation.toString()); + + // Update text widget with R result + Text_Correlation.setText("Correlation: " + correlation.toFixed(3)); +} + +/** + * Pattern 37: Write to R environment from JavaScript + * Use when: Passing user input to R script + */ +function writeToREnvironment() { + // Get user selection + var userSelection = Dropdown_1.getSelectedKey(); + var numValue = ConvertUtils.stringToInteger(userSelection); + + // Pass to R environment + RVisualization_1 + .getInputParameters() + .setNumber("userSelection", numValue); + + // R script can now use: userSelection variable +} + + +// ============================================================================= +// METHOD CHAINING PATTERNS +// ============================================================================= + +/** + * Pattern 38: Method chaining for concise code + * Use when: One-time data access, debugging + */ +function methodChainingExamples() { + // Chained logging (no intermediate variables) + console.log(Chart_1.getDataSource().getVariables()); + + // Chained filter application + Chart_1.getDataSource().setDimensionFilter("Year", "2024"); + + // Chained member access + var firstMember = Table_1.getDataSource() + .getMembers("Location", 1)[0]; + + // When NOT to chain: need to reuse result + var ds = Chart_1.getDataSource(); // Reuse ds + ds.setDimensionFilter("Year", "2024"); + ds.setDimensionFilter("Region", "EMEA"); + ds.refreshData(); +} + + +// ============================================================================= +// EQUALITY COMPARISON PATTERNS +// ============================================================================= + +/** + * Pattern 39: Using strict equality (recommended) + * SAC supports === always, == only with same types + */ +function equalityComparison() { + var aNumber = 1; + var aString = "1"; + + // CORRECT: Strict equality (always works) + if (aNumber === 1) { + console.log("Number equals 1"); + } + + if (aString === "1") { + console.log("String equals '1'"); + } + + // CORRECT: Compare same types + if (aString == "1") { + console.log("String comparison works"); + } + + // WOULD ERROR: Different types with == + // if (aNumber == "1") { } // Don't do this! +} + + +// ============================================================================= +// DATASOURCE INFO PATTERNS +// ============================================================================= + +/** + * Pattern 40: Get data source metadata + * Use when: Need model information for logging or display + */ +function getDataSourceInfo() { + var dsInfo = Table_1.getDataSource().getInfo(); + + console.log("Model: " + dsInfo.modelName); + console.log("Model ID: " + dsInfo.modelId); + console.log("Description: " + dsInfo.modelDescription); + + // SAP BW only properties + if (dsInfo.sourceName !== undefined) { + console.log("Source: " + dsInfo.sourceName); + console.log("Last changed by: " + dsInfo.sourceLastChangedBy); + + if (dsInfo.sourceLastRefreshedAt) { + console.log("Last refresh: " + + dsInfo.sourceLastRefreshedAt.toISOString()); + } + } + + // Display in UI + Text_ModelInfo.setText("Model: " + dsInfo.modelName); +} diff --git a/templates/planning-operations.js b/templates/planning-operations.js new file mode 100644 index 0000000..86e28c9 --- /dev/null +++ b/templates/planning-operations.js @@ -0,0 +1,587 @@ +/** + * SAP Analytics Cloud - Planning Operations Patterns + * + * Ready-to-use code patterns for SAC Planning applications. + * Copy and adapt these patterns to your application. + * + * Source: SAP Analytics Cloud Scripting Skill + * Version: 2025.14+ + */ + +// ============================================================================= +// VERSION MANAGEMENT PATTERNS +// ============================================================================= + +/** + * Pattern 1: Get and display version info + * Use in: Application initialization or info button + */ +function displayVersionInfo() { + var planning = Table_1.getPlanning(); + + var publicVersions = planning.getPublicVersions(); + var privateVersions = planning.getPrivateVersions(); + + console.log("Public Versions:", publicVersions.length); + console.log("Private Versions:", privateVersions.length); + + publicVersions.forEach(function(v) { + console.log("Public: " + v.id); + }); +} + +/** + * Pattern 2: Publish version with validation + * Use in: Publish button onClick + */ +function publishVersion(versionId) { + Application.showBusyIndicator(); + + try { + var version = Table_1.getPlanning().getPublicVersion(versionId); + + if (!version) { + Application.showMessage( + ApplicationMessageType.Error, + "Version '" + versionId + "' not found" + ); + return false; + } + + if (!version.isDirty()) { + Application.showMessage( + ApplicationMessageType.Info, + "No changes to publish" + ); + return false; + } + + version.publish(); + + Application.showMessage( + ApplicationMessageType.Success, + "Version published successfully" + ); + + // Refresh data + Table_1.getDataSource().refreshData(); + + return true; + + } catch (error) { + console.log("Publish error:", error); + Application.showMessage( + ApplicationMessageType.Error, + "Failed to publish: " + error + ); + return false; + + } finally { + Application.hideBusyIndicator(); + } +} + +/** + * Pattern 3: Copy public version to new private version + * Use in: Create working copy button + */ +function createWorkingCopy(sourceVersionId) { + Application.showBusyIndicator(); + + try { + var sourceVersion = Table_1.getPlanning().getPublicVersion(sourceVersionId); + + if (!sourceVersion) { + Application.showMessage( + ApplicationMessageType.Error, + "Source version not found" + ); + return null; + } + + var timestamp = Date.now().toString(); + var newVersionId = "WorkingCopy_" + timestamp; + + sourceVersion.copy(newVersionId, PlanningCopyOption.AllData); + + Application.showMessage( + ApplicationMessageType.Success, + "Working copy created: " + newVersionId + ); + + return newVersionId; + + } catch (error) { + console.log("Copy error:", error); + Application.showMessage( + ApplicationMessageType.Error, + "Failed to create copy" + ); + return null; + + } finally { + Application.hideBusyIndicator(); + } +} + +/** + * Pattern 4: Copy with specific category (Budget/Forecast) + * Use in: Category-specific copy operations + */ +function copyAsCategory(sourceVersionId, targetVersionId, category) { + Application.showBusyIndicator(); + + try { + var sourceVersion = Table_1.getPlanning().getPrivateVersion(sourceVersionId); + + if (!sourceVersion) { + sourceVersion = Table_1.getPlanning().getPublicVersion(sourceVersionId); + } + + if (!sourceVersion) { + Application.showMessage( + ApplicationMessageType.Error, + "Source version not found" + ); + return false; + } + + // Copy with category + sourceVersion.copy( + targetVersionId, + PlanningCopyOption.AllData, + category // PlanningCategory.Budget or PlanningCategory.Forecast + ); + + Application.showMessage( + ApplicationMessageType.Success, + "Version copied successfully" + ); + + return true; + + } catch (error) { + console.log("Copy error:", error); + Application.showMessage( + ApplicationMessageType.Error, + "Failed to copy version" + ); + return false; + + } finally { + Application.hideBusyIndicator(); + } +} + +/** + * Pattern 5: Revert unsaved changes + * Use in: Discard changes button + */ +function revertChanges(versionId) { + var version = Table_1.getPlanning().getPublicVersion(versionId); + + if (!version) { + Application.showMessage( + ApplicationMessageType.Error, + "Version not found" + ); + return; + } + + if (!version.isDirty()) { + Application.showMessage( + ApplicationMessageType.Info, + "No changes to revert" + ); + return; + } + + version.revert(); + Table_1.getDataSource().refreshData(); + + Application.showMessage( + ApplicationMessageType.Info, + "Changes reverted" + ); +} + +/** + * Pattern 6: Delete private version + * Use in: Delete version button (with confirmation) + */ +function deletePrivateVersion(versionId) { + var version = Table_1.getPlanning().getPrivateVersion(versionId); + + if (!version) { + Application.showMessage( + ApplicationMessageType.Error, + "Private version not found" + ); + return false; + } + + try { + version.delete(); + Application.showMessage( + ApplicationMessageType.Success, + "Version deleted" + ); + return true; + } catch (error) { + Application.showMessage( + ApplicationMessageType.Error, + "Failed to delete version" + ); + return false; + } +} + + +// ============================================================================= +// DATA LOCKING PATTERNS +// ============================================================================= + +/** + * Pattern 7: Check lock state before edit + * Use before: Any data modification + */ +function checkLockState() { + var selections = Table_1.getSelections(); + + if (selections.length === 0) { + Application.showMessage( + ApplicationMessageType.Warning, + "Please select a cell first" + ); + return null; + } + + var lockState = Table_1.getPlanning() + .getDataLocking() + .getState(selections[0]); + + return lockState; +} + +/** + * Pattern 8: Lock state validation with message + * Use in: Edit button onClick + */ +function validateAndEdit() { + var lockState = checkLockState(); + + if (lockState === null) { + return; + } + + switch (lockState) { + case DataLockingState.Locked: + Application.showMessage( + ApplicationMessageType.Warning, + "Data is locked by another user. Cannot edit." + ); + break; + + case DataLockingState.LockedByMe: + Application.showMessage( + ApplicationMessageType.Info, + "Data is locked by you. Proceeding with edit." + ); + performEdit(); + break; + + case DataLockingState.Unlocked: + performEdit(); + break; + } +} + +function performEdit() { + // Implement edit logic + console.log("Performing edit..."); +} + + +// ============================================================================= +// DATA ACTION PATTERNS +// ============================================================================= + +/** + * Pattern 9: Execute data action with parameters + * Use in: Run data action button + */ +function runDataAction(year, version) { + Application.showBusyIndicator(); + + // Set parameters + DataAction_Allocation.setParameterValue("YEAR", year); + DataAction_Allocation.setParameterValue("VERSION", version); + + // Execute + DataAction_Allocation.execute(); +} + +// Data action event handlers +DataAction_Allocation.onBeforeExecute = function() { + console.log("Data action starting..."); +}; + +DataAction_Allocation.onAfterExecute = function(result) { + Application.hideBusyIndicator(); + + if (result.success) { + Table_1.getDataSource().refreshData(); + Application.showMessage( + ApplicationMessageType.Success, + "Data action completed successfully" + ); + } else { + Application.showMessage( + ApplicationMessageType.Error, + "Data action failed: " + (result.message || "Unknown error") + ); + } +}; + +/** + * Pattern 10: Data action with dropdown parameters + * Use in: Dynamic data action execution + */ +function runDataActionFromDropdowns() { + var sourceVersion = Dropdown_SourceVersion.getSelectedKey(); + var targetVersion = Dropdown_TargetVersion.getSelectedKey(); + var year = Dropdown_Year.getSelectedKey(); + + if (!sourceVersion || !targetVersion || !year) { + Application.showMessage( + ApplicationMessageType.Warning, + "Please select all parameters" + ); + return; + } + + Application.showBusyIndicator(); + + DataAction_Copy.setParameterValue("SOURCE_VERSION", sourceVersion); + DataAction_Copy.setParameterValue("TARGET_VERSION", targetVersion); + DataAction_Copy.setParameterValue("YEAR", year); + + DataAction_Copy.execute(); +} + +/** + * Pattern 11: Background data action execution + * Use for: Long-running data actions + */ +function runDataActionInBackground() { + Application.showMessage( + ApplicationMessageType.Info, + "Data action started in background" + ); + + DataAction_LongRunning.executeInBackground(); +} + +DataAction_LongRunning.onExecutionStatusUpdate = function(status) { + console.log("Background status:", status); + + switch (status) { + case ExecutionStatus.Running: + console.log("Still running..."); + break; + + case ExecutionStatus.Completed: + Table_1.getDataSource().refreshData(); + Application.showMessage( + ApplicationMessageType.Success, + "Background task completed" + ); + break; + + case ExecutionStatus.Failed: + Application.showMessage( + ApplicationMessageType.Error, + "Background task failed" + ); + break; + + case ExecutionStatus.Cancelled: + Application.showMessage( + ApplicationMessageType.Info, + "Background task cancelled" + ); + break; + } +}; + + +// ============================================================================= +// PLANNING WORKFLOW PATTERNS +// ============================================================================= + +/** + * Pattern 12: Complete planning workflow + * Create copy → Edit → Publish + */ +var WorkflowState = { + workingVersion: null +}; + +// Step 1: Create working copy +function startPlanningWorkflow(sourceVersionId) { + var newVersionId = createWorkingCopy(sourceVersionId); + if (newVersionId) { + WorkflowState.workingVersion = newVersionId; + Table_1.getDataSource().setDimensionFilter("Version", newVersionId); + Application.showMessage( + ApplicationMessageType.Info, + "Editing version: " + newVersionId + ); + } +} + +// Step 2: Save progress +function savePlanningProgress() { + if (!WorkflowState.workingVersion) { + Application.showMessage( + ApplicationMessageType.Warning, + "No active working version" + ); + return; + } + + Table_1.getPlanning().submitData(); + Application.showMessage( + ApplicationMessageType.Success, + "Changes saved" + ); +} + +// Step 3: Complete workflow +function completePlanningWorkflow() { + if (!WorkflowState.workingVersion) { + Application.showMessage( + ApplicationMessageType.Warning, + "No active working version" + ); + return; + } + + var version = Table_1.getPlanning().getPrivateVersion(WorkflowState.workingVersion); + if (version && version.isDirty()) { + version.publish(); + } + + WorkflowState.workingVersion = null; + Application.showMessage( + ApplicationMessageType.Success, + "Planning workflow completed" + ); +} + +/** + * Pattern 13: Find active planning cycle from attribute + * Use in: Auto-filter to current planning cycle + */ +function findActivePlanningCycle() { + var allCycles = PlanningModel_1.getMembers("PlanningCycle"); + + for (var i = 0; i < allCycles.length; i++) { + // Check for active flag attribute + if (allCycles[i].properties.IsActive === "X" || + allCycles[i].properties.Flag === "PC+0") { + return allCycles[i].id; + } + } + + return null; +} + +/** + * Pattern 14: Set year filter based on planning cycle + * Use in: Dynamic year filtering + */ +function setYearFromPlanningCycle() { + var activeCycle = findActivePlanningCycle(); + + if (activeCycle) { + // Format the filter value + var yearFilter = "[Date].[YQM].&[" + activeCycle + "]"; + + Table_1.getDataSource().setDimensionFilter("Date", yearFilter); + + console.log("Active planning cycle set:", activeCycle); + } +} + + +// ============================================================================= +// INPUT FORM PATTERNS +// ============================================================================= + +/** + * Pattern 15: Submit data after input + * Use in: Save button for input forms + */ +function submitInputData() { + Application.showBusyIndicator(); + + try { + // Submit pending changes + Table_1.getPlanning().submitData(); + + // Get current version and publish + var version = Table_1.getPlanning().getPublicVersion("Budget2024"); + if (version && version.isDirty()) { + version.publish(); + } + + Application.showMessage( + ApplicationMessageType.Success, + "Data submitted successfully" + ); + + } catch (error) { + console.log("Submit error:", error); + Application.showMessage( + ApplicationMessageType.Error, + "Failed to submit data" + ); + } finally { + Application.hideBusyIndicator(); + } +} + +/** + * Pattern 16: Validate before submit + * Use in: Pre-submission validation + */ +function validateAndSubmit() { + // Check for required selections + if (!Dropdown_CostCenter.getSelectedKey()) { + Application.showMessage( + ApplicationMessageType.Warning, + "Please select a cost center" + ); + return; + } + + // Check lock state + var selections = Table_1.getSelections(); + if (selections.length > 0) { + var lockState = Table_1.getPlanning() + .getDataLocking() + .getState(selections[0]); + + if (lockState === DataLockingState.Locked) { + Application.showMessage( + ApplicationMessageType.Error, + "Data is locked. Cannot submit." + ); + return; + } + } + + // Proceed with submit + submitInputData(); +}