From 484fdd0c02f756320a59a958dbe4602292d75f10 Mon Sep 17 00:00:00 2001 From: Zhongwei Li Date: Sun, 30 Nov 2025 08:55:30 +0800 Subject: [PATCH] Initial commit --- .claude-plugin/plugin.json | 12 + README.md | 3 + SKILL.md | 749 ++++++++++++++ plugin.lock.json | 137 +++ references/advanced-formulas.md | 22 + references/ai-planning-analytics.md | 17 + references/allocations.md | 24 + references/analytics-designer-planning.md | 905 +++++++++++++++++ references/api-reference.md | 1054 ++++++++++++++++++++ references/api-snippets.md | 48 + references/data-actions.md | 520 ++++++++++ references/data-locking.md | 27 + references/input-tasks.md | 21 + references/javascript-patterns.md | 1081 +++++++++++++++++++++ references/job-monitoring.md | 21 + references/modeling-basics.md | 30 + references/multi-actions.md | 31 + references/planning-workflows.md | 543 +++++++++++ references/predictive-conversion.md | 14 + references/s4hana-acdocp-export.md | 257 +++++ references/scheduling-calendar.md | 29 + references/version-edit-modes.md | 22 + references/version-management.md | 495 ++++++++++ references/version-publishing-notes.md | 9 + templates/data-action-checklist.md | 10 + templates/multi-action-checklist.md | 10 + templates/parameter-table.md | 12 + 27 files changed, 6103 insertions(+) create mode 100644 .claude-plugin/plugin.json create mode 100644 README.md create mode 100644 SKILL.md create mode 100644 plugin.lock.json create mode 100644 references/advanced-formulas.md create mode 100644 references/ai-planning-analytics.md create mode 100644 references/allocations.md create mode 100644 references/analytics-designer-planning.md create mode 100644 references/api-reference.md create mode 100644 references/api-snippets.md create mode 100644 references/data-actions.md create mode 100644 references/data-locking.md create mode 100644 references/input-tasks.md create mode 100644 references/javascript-patterns.md create mode 100644 references/job-monitoring.md create mode 100644 references/modeling-basics.md create mode 100644 references/multi-actions.md create mode 100644 references/planning-workflows.md create mode 100644 references/predictive-conversion.md create mode 100644 references/s4hana-acdocp-export.md create mode 100644 references/scheduling-calendar.md create mode 100644 references/version-edit-modes.md create mode 100644 references/version-management.md create mode 100644 references/version-publishing-notes.md create mode 100644 templates/data-action-checklist.md create mode 100644 templates/multi-action-checklist.md create mode 100644 templates/parameter-table.md diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..4164d0c --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "sap-sac-planning", + "description": "Planning application development with SAC. Covers planning models, data actions, version management, data locking, calendar workflows, and getPlanning() API.", + "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..12ac3b6 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# sap-sac-planning + +Planning application development with SAC. Covers planning models, data actions, version management, data locking, calendar workflows, and getPlanning() API. diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..8853a5f --- /dev/null +++ b/SKILL.md @@ -0,0 +1,749 @@ +--- +name: sap-sac-planning +description: | + This skill should be used when developing SAP Analytics Cloud (SAC) planning applications, including building planning-enabled stories, analytics designer applications with planning functionality, data actions, multi actions, version management, and planning workflows. Use when creating planning models, implementing data entry forms, configuring spreading/distribution/allocation, setting up data locking, building calendar-based planning processes with approval workflows, writing JavaScript scripts for planning automation, using the getPlanning() API, PlanningModel API, or DataSource API for planning scenarios, troubleshooting planning performance issues, or integrating predictive forecasting into planning workflows. +license: GPL-3.0 +metadata: + version: 1.3.0 + last_verified: 2025-11-26 + sac_version: "2025.23" + documentation_source: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e) + api_reference: [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/2025.23/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/2025.23/en-US/index.html) + reference_files: 20 + status: production +--- + +# SAP Analytics Cloud Planning Skill + +Comprehensive skill for building enterprise planning applications with SAP Analytics Cloud. + +--- + +## Reference Add-Ons (2025.23) + +- Execution guides: `references/data-actions.md`, `references/multi-actions.md`, `references/allocations.md`, `references/scheduling-calendar.md`, `references/data-locking.md` +- Modeling & governance: `references/modeling-basics.md`, `references/version-management.md`, `references/version-edit-modes.md`, `references/version-publishing-notes.md` +- Calculations & intelligence: `references/advanced-formulas.md`, `references/predictive-conversion.md`, `references/ai-planning-analytics.md`, `references/api-snippets.md` +- Workflow aids: `references/input-tasks.md`, `references/job-monitoring.md` +- Ready-to-use templates: `templates/data-action-checklist.md`, `templates/multi-action-checklist.md`, `templates/parameter-table.md` + +Use these to keep instructions concise in this file while deep-dives remain one click away. + +--- + +## When to Use This Skill + +Use this skill when working on tasks involving: + +**Planning Application Development**: +- Creating planning-enabled stories with data entry +- Building analytics designer applications for planning +- Implementing input forms and planning tables +- Configuring planning models with Version and Date dimensions +- Setting up data sources for planning scenarios + +**Data Actions & Multi Actions**: +- Creating data actions for copy, allocation, and calculations +- Building multi actions to orchestrate planning operations +- Configuring parameters (member, number, string, datetime types) +- Implementing embedded data actions +- Setting up API steps for external integrations + +**Version Management**: +- Managing public and private versions +- Publishing workflows (Publish As, Publish Private Data) +- Sharing private versions with collaborators +- Version creation and deletion via API + +**Planning Workflows**: +- Setting up calendar-based planning processes +- Creating general tasks, review tasks, and composite tasks +- Implementing multi-level approval workflows +- Configuring data locking tasks +- Managing task dependencies + +**JavaScript Planning APIs**: +- Using getPlanning() API for data entry +- Working with PlanningModel API for master data +- Implementing DataSource API for filtering and querying +- Writing scripts for planning automation +- Handling version management via API + +**Data Entry & Allocation**: +- Implementing spreading (to child members) +- Configuring distribution (between siblings) +- Setting up rule-based allocations +- Copy/paste operations in planning tables +- Using advanced formulas for calculations + +**Data Locking**: +- Configuring data locking on models +- Setting up lock states (locked, restricted, open) +- Creating data locking tasks in calendar +- Implementing event-based data locking +- Integrating data locking in multi actions + +--- + +## Quick Start + +### Creating a Planning-Enabled Story + +1. **Create Planning Model** with required dimensions: + - Version dimension (required) + - Date dimension (required) + - Account dimension (recommended) + - Other business dimensions + +2. **Add Table Widget** to story and link to planning model + +3. **Enable Planning** on the table: + - Select table → Planning panel → Enable Planning + - Configure version selection (public or private) + +4. **Configure Data Entry**: + - Set editable measures/accounts + - Configure spreading behavior + - Set up validation rules + +### Creating an Analytics Designer Planning Application + +1. **Create Analytic Application** (not Optimized Story) +2. **Add Planning Model** as data source +3. **Add Table Widget** and enable planning +4. **Write Scripts** for: + - Version selection + - Data submission + - Custom validation + - Workflow triggers + +--- + +## Core Concepts + +### Model Types + +**Planning Model**: +- Supports data write-back +- Requires Version and Date dimensions +- Enables spreading, distribution, allocation +- Supports data locking +- Used for budgeting, forecasting, planning + +**Analytic Model**: +- Read-only (no write-back) +- No required dimensions +- Better performance for reporting +- Use when planning not needed + +### Version Management + +**Public Versions**: +- Visible to all users with access +- Shared across the organization +- Require publish to update + +**Private Versions**: +- Visible only to creator (unless shared) +- Used for simulation and what-if analysis +- Can be published to public or new version + +**Edit Mode**: +- Temporary private copy when editing public version +- Changes visible only to editor until published +- Automatic validation on publish + +**Reference**: See `references/version-management.md` for detailed workflows. + +### Planning API Overview + +**getPlanning() API** - Table planning operations: +```javascript +// Check if planning is enabled +var isEnabled = Table_1.getPlanning().isEnabled(); + +// Get public versions +var publicVersions = Table_1.getPlanning().getPublicVersions(); + +// Get private version +var privateVersion = Table_1.getPlanning().getPrivateVersion(); + +// Set user input (data entry) +Table_1.getPlanning().setUserInput(selection, value); + +// Submit data changes +Table_1.getPlanning().submitData(); +``` + +**PlanningModel API** - Master data operations: +```javascript +// Get dimension members with properties +var members = PlanningModel_1.getMembers("CostCenter"); + +// Create new members +PlanningModel_1.createMembers("CostCenter", [ + {id: "CC100", description: "Marketing"} +]); + +// Update existing members +PlanningModel_1.updateMembers("CostCenter", [ + {id: "CC100", description: "Marketing Dept"} +]); + +// Delete members +PlanningModel_1.deleteMembers("CostCenter", ["CC100"]); +``` + +**DataSource API** - Filtering and querying: +```javascript +// Set dimension filter +Table_1.getDataSource().setDimensionFilter("Version", + "[Version].[parentId].&[public.Actual]"); + +// Get members with booked values only +var members = Table_1.getDataSource().getMembers("Account", + {accessMode: MemberAccessMode.BookedValues}); + +// Remove filter +Table_1.getDataSource().removeDimensionFilter("Version"); +``` + +**Reference**: See `references/api-reference.md` for complete API documentation. + +--- + +## Data Actions + +Data actions perform calculations and data manipulation on planning models. + +### Step Types + +| Step Type | Purpose | +|-----------|---------| +| Copy | Move data between dimensions/versions | +| Advanced Formula | Complex calculations | +| Allocation | Rule-based distribution | +| Currency Conversion | Convert currencies | +| Embedded Data Action | Run another data action | + +### Creating a Copy Step + +``` +Source: + Version = Actual + Year = 2024 + +Target: + Version = Budget + Year = 2025 + +Mapping: + Account = Account (same) + CostCenter = CostCenter (same) +``` + +### Advanced Formula Example + +``` +// Calculate forecast = Actual + (Budget - Actual) * 0.5 +[Version].[Forecast] = [Version].[Actual] + + ([Version].[Budget] - [Version].[Actual]) * 0.5 +``` + +### Parameters + +Add parameters to make data actions reusable: +- **Member Parameter**: Select dimension member +- **Number Parameter**: Enter numeric value +- **String Parameter**: Enter text (2025+) +- **Datetime Parameter**: Select date/time (2025+) + +**Reference**: See `references/data-actions.md` for complete configuration guide. + +--- + +## Multi Actions + +Multi actions orchestrate multiple planning operations across models and versions. + +### Available Step Types + +1. **Data Action Step**: Run data action with parameters +2. **Version Management Step**: Publish versions +3. **Predictive Step**: Run forecasting scenarios +4. **Data Import Step**: Import from SAP sources +5. **API Step**: Call external HTTP APIs +6. **Data Locking Step**: Lock/unlock data slices +7. **PaPM Step**: Run Profitability and Performance Management + +### Example Multi Action Flow + +``` +1. Clean target version (Data Action) +2. Import actuals (Data Import) +3. Run forecast (Predictive) +4. Calculate allocations (Data Action) +5. Publish to public version (Version Management) +6. Lock published data (Data Locking) +``` + +### Cross-Model Parameters + +When using public dimensions, create cross-model parameters to share values across steps in different models. + +**Reference**: See `references/data-actions.md` for multi action configuration. + +--- + +## S/4HANA ACDOCP Export + +Export native planning data from SAC to SAP S/4HANA's ACDOCP table (central ERP plan data storage). + +### Architecture + +``` +SAC Planning Model → Data Export Service → Cloud Connector → API_PLPACDOCPDATA_SRV → ACDOCP +``` + +### Prerequisites + +| Requirement | Details | +|-------------|---------| +| **Legacy Mode** | Must be enabled on planning model | +| **OData Service** | Activate `API_PLPACDOCPDATA_SRV` in `/IWFND/MAINT_SERVICE` | +| **Cloud Connector** | Required for on-premise S/4HANA | + +### Required Dimensions for Export + +- **Version (Plan Category)**: Only public versions can be exported +- **FiscalYearPeriod**: Mandatory in export scope +- **Measure**: Only ONE target measure per export job +- **G/L Account**: Required for ACDOCP mapping + +### Export Behavior + +- Exported data **overwrites existing data within scope** +- S/4HANA generates delta records for changes +- **Deletions don't propagate**: Set values to 0 and re-export to clear ACDOCP data +- Filters cannot be changed after export job creation—name jobs descriptively + +### Quick Setup + +1. Enable Legacy Mode on planning model +2. Create S/4HANA connection with Cloud Connector +3. Data Management → Create Data Export Job +4. Map dimensions to ACDOCP fields +5. Define export scope (FiscalYearPeriod + PlanningCategory mandatory) +6. Schedule or run export + +**Reference**: See `references/s4hana-acdocp-export.md` for complete configuration guide, troubleshooting, and SAP documentation links. + +--- + +## Planning Workflows (Calendar) + +The SAP Analytics Cloud calendar organizes collaborative planning processes. + +### Task Types + +**General Task**: Data entry by assignees +- Attach work file (story/application) +- Set due dates and notifications +- Track completion status + +**Review Task**: Approval workflow +- Review results of general tasks +- Approve or reject submissions +- Automatic notification on status change + +**Composite Task**: Combined entry and review +- Simplified approval for single-level workflows +- Driving dimension support for regional planning + +**Data Locking Task**: Schedule lock changes +- Specify data slice to lock/unlock +- Set target lock state +- Event-based triggering + +### Multi-Level Approval + +``` +Round 1: Regional Managers review regional plans + ↓ (on approval) +Round 2: Finance Director reviews consolidated plan + ↓ (on approval) +Round 3: CFO final approval + ↓ (on approval) +Data Locking: Lock approved plan data +``` + +### Task Dependencies + +Configure predecessor tasks to create sequential workflows: +- Review tasks automatically start when predecessor completes +- Data locking tasks trigger on approval events + +**Reference**: See `references/planning-workflows.md` for calendar configuration. + +--- + +## Spreading & Distribution + +### Spreading (Vertical) + +Distributes values from parent to child members: +- **Equal Spread**: Divide equally among children +- **Proportional Spread**: Maintain existing ratios +- **Automatic**: SAC determines best method + +```javascript +// Spreading happens automatically when entering at aggregate level +// Example: Enter 1000 at "Total Regions" spreads to child regions +``` + +### Distribution (Horizontal) + +Moves values between members at same hierarchy level: +- Select source and target cells +- Choose distribution method +- Apply via context menu or script + +### Allocation by Rules + +Configure structured allocations in data actions: +- Define driver accounts for percentage distribution +- Set allocation targets +- Execute via data action or multi action + +--- + +## Data Locking + +Protect planning data during and after planning cycles. + +### Lock States + +| State | Data Entry | Owner Can Edit | +|-------|------------|----------------| +| Open | Yes | Yes | +| Restricted | No (except owner) | Yes | +| Locked | No | No | +| Mixed | Varies | Varies (selection contains multiple states) | + +### Configuration + +1. **Enable Data Locking** on planning model +2. **Define Driving Dimensions** (e.g., Region, Version) +3. **Assign Owners** to data slices +4. **Configure Lock Regions** via model settings + +### Script Example + +```javascript +// Get data locking object +var dataLocking = Table_1.getPlanning().getDataLocking(); + +// Get lock state for selection +var selection = Table_1.getSelections()[0]; +var lockState = dataLocking.getState(selection); + +// Check if locked +if (lockState === DataLockingState.Locked) { + Application.showMessage("This data is locked."); +} +``` + +**Reference**: See `references/planning-workflows.md` for data locking patterns. + +--- + +## Members on the Fly + +Create, update, and delete dimension members dynamically at runtime. + +### Supported Operations + +```javascript +// Create new member +PlanningModel_1.createMembers("CostCenter", { + id: "CC_NEW", + description: "New Cost Center" +}); + +// Update existing member +PlanningModel_1.updateMembers("CostCenter", { + id: "CC_NEW", + description: "Updated Description" +}); + +// Get single member +var member = PlanningModel_1.getMember("CostCenter", "CC_NEW"); + +// Get members with pagination +var members = PlanningModel_1.getMembers("CostCenter", { + offset: "0", + limit: "100" +}); +``` + +### Important Restrictions + +- **Dimension Type**: Only "Generic" dimensions supported (NOT Account, Version, Time, Organization) +- **Refresh Required**: Call `Application.refreshData()` after member changes +- **Custom Properties**: Use prefixes to avoid naming conflicts (e.g., "CUSTOM_Region") + +**Reference**: See `references/analytics-designer-planning.md` for complete API documentation. + +--- + +## Common JavaScript Patterns + +### Finding Active Version by 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); +``` + +### Setting Filter from Planning Cycle + +```javascript +Application.showBusyIndicator(); +Table_1.setVisible(false); + +// Find active planning cycle +var cycles = PlanningModel_1.getMembers("PlanningCycle"); +var activeCycle = ""; + +for (var i = 0; i < cycles.length; i++) { + if (cycles[i].properties.Flag === "ACTIVE") { + activeCycle = cycles[i].id; + break; + } +} + +// Apply MDX filter +Table_1.getDataSource().setDimensionFilter("Date", + "[Date].[YQM].&[" + activeCycle + "]"); + +Table_1.setVisible(true); +Application.hideBusyIndicator(); +``` + +### Version Publishing + +```javascript +// Get forecast version +var forecastVersion = Table_1.getPlanning().getPublicVersion("Forecast2025"); + +// Check if changes need publishing +if (forecastVersion.isDirty()) { + forecastVersion.publish(); + Application.showMessage("Version published successfully."); +} +``` + +### Data Action Execution + +```javascript +// Execute data action with parameters +DataAction_1.setParameterValue("Version", "Budget"); +DataAction_1.setParameterValue("Year", "2025"); + +DataAction_1.execute(); + +// Or execute in background +DataAction_1.executeInBackground(); +``` + +**Reference**: See `references/javascript-patterns.md` for more examples. + +--- + +## Performance Best Practices + +### Data Action Optimization + +1. **Use Input Controls**: Link to parameters to reduce data scope +2. **Embed Related Actions**: Combine actions on same model/version +3. **Minimize Cross-Model Operations**: Keep data in single model when possible +4. **Use Batch Processing**: Group operations in single transaction + +### Story Performance + +1. **Enable Data Locking Selectively**: Only on models that need it +2. **Use Growing Mode**: For large tables with pagination +3. **Limit Visible Dimensions**: Reduce data cells displayed +4. **Optimize Filters**: Apply story filters before data entry + +### API Performance + +1. **Use Booked Values Filter**: Retrieve only posted data +2. **Limit getMembers() Results**: Set limit parameter +3. **Cache Member Lists**: Store in script variables when reusing +4. **Use Busy Indicator**: Improve perceived performance + +--- + +## Troubleshooting + +### Issue: Data not saving + +**Check**: +1. Planning enabled on table? +2. User has planning permissions? +3. Data locked? +4. Validation rules failing? + +**Debug**: +```javascript +console.log("Planning enabled: " + Table_1.getPlanning().isEnabled()); +var lockState = Table_1.getPlanning().getDataLocking().getState(selection); +console.log("Lock state: " + lockState); +``` + +### Issue: Version not publishing + +**Check**: +1. Valid changes only? (Invalid changes discarded) +2. Data access control allowing write? +3. Version not already published? + +### Issue: Data action failing + +**Check**: +1. Source data exists? +2. Target version writable? +3. Dimension mappings correct? +4. Parameters set correctly? + +**Debug**: Use data action tracing table with "Show Only Leaves" option. + +### Issue: getMembers() returns empty + +**Check**: +1. Dimension name correct? +2. Model connected? +3. User has read access? +4. Using correct API (PlanningModel vs DataSource)? + +--- + +## Official Documentation Links + +**Essential Resources**: +- **SAP Analytics Cloud Help (2025.23)**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e) +- **API Reference (2025.23)**: [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/2025.23/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/2025.23/en-US/index.html) +- **Analytics Designer Overview**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/0798b81f9130425389dec84e19326b93.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/0798b81f9130425389dec84e19326b93.html) +- **Planning Overview**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/cd897576c3344475a208c2f7a52f151e.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/cd897576c3344475a208c2f7a52f151e.html) + +**Planning Model & Data**: +- **Planning Model Data**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/bc9f0eb2da1848dd9d3925ec29337e9f.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/bc9f0eb2da1848dd9d3925ec29337e9f.html) +- **Model Overview**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/0ace2c43b92b41099b1cd964b4ff198a.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/0ace2c43b92b41099b1cd964b4ff198a.html) +- **Data Foundation**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/6f6e75a5e60a4d099939196a97a25814.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/6f6e75a5e60a4d099939196a97a25814.html) + +**Data Actions & Multi Actions**: +- **Run Data Actions**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/69a370e6cfd84315973101389baacde0.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/69a370e6cfd84315973101389baacde0.html) +- **Get Started with Data Actions**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/2850221adef14958a4554ad2860ff412.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/2850221adef14958a4554ad2860ff412.html) +- **Create Data Action**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/e28c7a30978b406aa5e24318206f6443.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/e28c7a30978b406aa5e24318206f6443.html) +- **Add Parameters**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/4835429d35534add875bae17e93b12e1.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/4835429d35534add875bae17e93b12e1.html) + +**Version Management**: +- **Version Management Overview**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/9d9056a13b764ad3aca8fef2630fcc00.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/9d9056a13b764ad3aca8fef2630fcc00.html) +- **Creating Versions**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/3b7f87c3d9cb49b7a6fef3f5cb0a6250.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/3b7f87c3d9cb49b7a6fef3f5cb0a6250.html) +- **Public Versions**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/b6e3d093988e4c3eba7eb6c1c110e954.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/b6e3d093988e4c3eba7eb6c1c110e954.html) +- **Private Versions**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/1a011f8041a84e109a3b6bf8c1c81bc1.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/1a011f8041a84e109a3b6bf8c1c81bc1.html) + +**Data Locking**: +- **Configuring Data Locking**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/e07d46e950794d5a928a9b16d1394de6.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/e07d46e950794d5a928a9b16d1394de6.html) +- **Data Locking States**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/91fa3cbbd46d457ab04f9ef3c7901655.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/91fa3cbbd46d457ab04f9ef3c7901655.html) + +**Calendar & Workflows**: +- **Calendar Overview**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/af4b7e39edd249d3b59fa7d4ab110a7a.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/af4b7e39edd249d3b59fa7d4ab110a7a.html) +- **Planning Processes**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/f6189755175940f3a4e007c3d6b83ee5.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/f6189755175940f3a4e007c3d6b83ee5.html) +- **Task Types**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/32c739d6f05b4990a08ef3948b18a1aa.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/32c739d6f05b4990a08ef3948b18a1aa.html) + +**Allocations & Spreading**: +- **Allocation Overview**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/de944ce1189543e5858798036d576094.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/de944ce1189543e5858798036d576094.html) +- **Value Driver Trees**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/b4d2b021719f4d958afd0922ac7de8d1.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/b4d2b021719f4d958afd0922ac7de8d1.html) + +**Learning Resources**: +- **Planning Learning Journey**: [https://learning.sap.com/learning-journeys/leveraging-sap-analytics-cloud-functionality-for-enterprise-planning](https://learning.sap.com/learning-journeys/leveraging-sap-analytics-cloud-functionality-for-enterprise-planning) +- **Advanced Planning Course**: [https://learning.sap.com/courses/leveraging-advanced-features-in-sap-analytics-cloud-for-planning](https://learning.sap.com/courses/leveraging-advanced-features-in-sap-analytics-cloud-for-planning) + +--- + +## Bundled Reference Files + +This skill includes comprehensive reference documentation (20 files): + +**API & Scripting**: +1. **references/api-reference.md**: Complete Analytics Designer API for planning +2. **references/analytics-designer-planning.md**: Planning scripting, setUserInput, versions, data locking, members on the fly +3. **references/api-snippets.md**: Quick API code examples and snippets + +**Core Planning Features**: +4. **references/data-actions.md**: Data Actions, Multi Actions, parameters, steps +5. **references/multi-actions.md**: Orchestrate multiple planning operations +6. **references/allocations.md**: Rule-based distribution and allocations +7. **references/advanced-formulas.md**: Complex calculations and formulas +8. **references/predictive-conversion.md**: Predictive forecasting integration + +**Workflow & Collaboration**: +9. **references/planning-workflows.md**: Calendar, tasks, approvals, data locking +10. **references/scheduling-calendar.md**: Planning calendar setup +11. **references/input-tasks.md**: Collaborative data entry tasks +12. **references/job-monitoring.md**: Track data action execution + +**Version Management**: +13. **references/version-management.md**: Versions, publishing, sharing, edit mode +14. **references/version-edit-modes.md**: Version editing workflows +15. **references/version-publishing-notes.md**: Publishing best practices + +**Integration & Advanced**: +16. **references/s4hana-acdocp-export.md**: S/4HANA integration, ACDOCP export, OData setup +17. **references/ai-planning-analytics.md**: AI-powered planning features + +**Development**: +18. **references/javascript-patterns.md**: Code snippets, patterns, best practices +19. **references/modeling-basics.md**: Planning model fundamentals +20. **references/data-locking.md**: Configure and manage data locks + +--- + +## Instructions for Claude + +When using this skill: + +1. **Check model type first** - Ensure planning model (not analytic) for write operations +2. **Verify planning enabled** - Table must have planning enabled +3. **Use appropriate API** - PlanningModel for master data, getPlanning() for transactions +4. **Handle versions correctly** - Private for drafts, publish to public when ready +5. **Respect data locking** - Check lock state before suggesting edits +6. **Use busy indicators** - For long operations to improve UX +7. **Follow MDX syntax** - For dimension filters: `[Dim].[Hierarchy].&[Member]` +8. **Test data actions** - Use tracing before production deployment +9. **Consider performance** - Apply filters to reduce data scope +10. **Link to documentation** - Include relevant SAP Help links in responses + +For troubleshooting: +- Check console for script errors +- Verify model connectivity +- Review data action logs +- Test with simplified scenarios first +- Check user permissions and data access + +--- + +**License**: GPL-3.0 +**Version**: 1.3.0 +**Maintained by**: SAP Skills Maintainers +**Repository**: [https://github.com/secondsky/sap-skills](https://github.com/secondsky/sap-skills) diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..20e7c10 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,137 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:secondsky/sap-skills:skills/sap-sac-planning", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "6f29a78679fafea2387f96f7071d832ebdae67ca", + "treeHash": "1d95e836f2f1bb8f632e6918ef808dc3f87bf543d681512f8e1dbc2a34ba65c8", + "generatedAt": "2025-11-28T10:28:14.492271Z", + "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-planning", + "description": "Planning application development with SAC. Covers planning models, data actions, version management, data locking, calendar workflows, and getPlanning() API.", + "version": "1.1.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "50e0676207c69e2e19c83f42f0e750f608a4f0081ff34e037beaa74f4e62b839" + }, + { + "path": "SKILL.md", + "sha256": "490c25d7e92879006f6ed18fc96c27d0e969a5dfc8bd2c4745a542bd70c23f08" + }, + { + "path": "references/modeling-basics.md", + "sha256": "dcf66cd8d406ec17e837769b22a1129bec1e847911339df8f886151c27fbf4b9" + }, + { + "path": "references/predictive-conversion.md", + "sha256": "7abbe9819ca03acccda1a0a4e477cf5cf3ee4347d95006e4b8cef8a228b75002" + }, + { + "path": "references/advanced-formulas.md", + "sha256": "bbd3e31e55db43c8ae5a05b074335e0e05722c375b3fd0525b96ac43ab842bd5" + }, + { + "path": "references/api-snippets.md", + "sha256": "955ee8ab11376302bf7a24a60d232523a682c19c7202f57fe342dcff6cf302f7" + }, + { + "path": "references/data-locking.md", + "sha256": "ba8ac27b554cd7a796e0362620994935f2c19cf0b472c107e50d49fa1be06407" + }, + { + "path": "references/job-monitoring.md", + "sha256": "55ab5970f718b2f3339b6cf6a250b75f26f8652f7bf4b9d02eb513254dbc98ba" + }, + { + "path": "references/input-tasks.md", + "sha256": "d262a1ed15f2b1b206de3f1af7d490b805121f51741ca59eff1888d1fc0d95c9" + }, + { + "path": "references/planning-workflows.md", + "sha256": "6cbb30de63a8baab4ddb3b65fb9f680ca3d2ed2eaa5469fa190c89e486e38453" + }, + { + "path": "references/allocations.md", + "sha256": "0aab5e191bd5c92b81cf3f90443aca6bf488432d0c00ebc2dfbd57c2db278e4c" + }, + { + "path": "references/version-publishing-notes.md", + "sha256": "624a59a6247158ede6f70e0bb81bc671ca7f7e986630a5a693365b495f023cce" + }, + { + "path": "references/multi-actions.md", + "sha256": "81effea1763f1fca8d976aba22952b20ee41b0b61cc1cfc9c4b7b6419ad50c47" + }, + { + "path": "references/javascript-patterns.md", + "sha256": "45a935d581cc248336be7ad9e1ad65ff12096bc5e5d074caaa1ae17ecdf19a3c" + }, + { + "path": "references/ai-planning-analytics.md", + "sha256": "8c2295f602b57df224c7a9c3027a813907b68780e9b91d3ab65da767085779b7" + }, + { + "path": "references/analytics-designer-planning.md", + "sha256": "c9ecdd772a2a9a2358e0f515601b509a736a08175887764ed8d6ac26a00516b0" + }, + { + "path": "references/s4hana-acdocp-export.md", + "sha256": "5407aa6e68487f26e22253d26e529f1afdda029f0e3a455ace2a331bc1e7b7b9" + }, + { + "path": "references/data-actions.md", + "sha256": "0243e2bc027980437beafa1f4e163915d98bb3e8dc7976eac0ef5a010eb28f58" + }, + { + "path": "references/version-management.md", + "sha256": "f1075cadd8208a5d494e0b6bb6e55048975e69330b104d7ad67065dc31defaa6" + }, + { + "path": "references/scheduling-calendar.md", + "sha256": "840c995f72d2be7f5505d41b97762f1431714fb38dc001d8b5a214abed0a0712" + }, + { + "path": "references/api-reference.md", + "sha256": "c4c54019e90183ac274b5317613bf7bf72874334cd8490412aa91549f14651ed" + }, + { + "path": "references/version-edit-modes.md", + "sha256": "4b5e9c6531695ef1bae8f153143a2eddcdfbeccef207b06c0625a33751e67b4f" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "4680615dfe6980cbca6b3aec957843964e9f17742f4b9d3dfc5429919a1383c0" + }, + { + "path": "templates/data-action-checklist.md", + "sha256": "07c70a406aab4c8148e84b09598583d416ef7f7417f4f378581219a1d2680fd8" + }, + { + "path": "templates/multi-action-checklist.md", + "sha256": "25381a4df8ef44d826b9410f471ab3463ed83e5b57d3cc2f2eb0b03dd981fda4" + }, + { + "path": "templates/parameter-table.md", + "sha256": "aba63ab5963be73d12f8c01b1ac01d02a327f3d145ede47d0ee7305e6c19b9dc" + } + ], + "dirSha256": "1d95e836f2f1bb8f632e6918ef808dc3f87bf543d681512f8e1dbc2a34ba65c8" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/references/advanced-formulas.md b/references/advanced-formulas.md new file mode 100644 index 0000000..646befb --- /dev/null +++ b/references/advanced-formulas.md @@ -0,0 +1,22 @@ +# Advanced Formulas (SAC 2025.23) + +Purpose: scripted calculations in data actions; choose visual editor for simple flows or script editor for full control. + +## Authoring modes +- **Visual**: guided blocks for scopes, loops, assignments (027-advanced-formulas-scripts.md, 028-advanced-formulas-visual.md). +- **Script**: free-form syntax; full reference in 029-advanced-formulas-script-reference.md. + +## Common patterns +- RESULTLOOKUP for driver retrieval; MEMBERSET for scoped loops; FOREACH for dimension iteration; IF/ENDIF for branching; DATA() for writes. +- Time offsets: use OFFSET on Date; handle week/month/year granularity explicitly. +- Exception aggregation: avoid mixing calculated members in copy rules where not supported. + +## Performance tips +- Narrow MEMBERSET scopes (time/version/org) before calculations. +- Use temporary variables; minimize writes inside loops. +- Prefer APPEND vs OVERWRITE intentionally; chunk large periods. + +## Testing +- Validate on small slice; check job monitor for syntax/validation errors. + +Sources: 027, 028, 029 advanced-formulas docs (SAC Help 2025.23). diff --git a/references/ai-planning-analytics.md b/references/ai-planning-analytics.md new file mode 100644 index 0000000..c358401 --- /dev/null +++ b/references/ai-planning-analytics.md @@ -0,0 +1,17 @@ +# AI for Planning & Analytics (SAC 2025.23) + +Source: 002-ai-planning-analytics.md. + +## Key capabilities +- **Just Ask / Search to Insight**: natural language queries; use for quick checks and to seed stories. +- **Smart Predict / Predictive Planning**: time-series forecasts feeding planning versions; pair with predictive steps in multi actions. +- **Smart Insights / Smart Discovery**: driver analysis to explain variances; useful before setting allocations. + +## When to apply in planning +- Pre-allocations: use Smart Insights to find drivers, then set allocation drivers accordingly. +- Forecast cycles: run predictive forecasts into private versions, validate, then publish. +- Ad-hoc questions during planning meetings with Just Ask to reduce manual slice-and-dice. + +## Guardrails +- Validate AI outputs before publish; keep in private versions for review. +- Ensure model permissions align; AI respects data access control. diff --git a/references/allocations.md b/references/allocations.md new file mode 100644 index 0000000..a46f8d7 --- /dev/null +++ b/references/allocations.md @@ -0,0 +1,24 @@ +# Allocations & Spreading (SAC 2025.23) + +Purpose: split source values across target dimension members using drivers or direct assignment within data actions. + +## When to use +- Allocate overheads or top-down plan values to cost centers/products/regions. +- Control booking to leaf members while preserving totals. + +## Setup (data action allocation step) +- **Source context**: filters + write mode (append/overwrite). +- **Driver context**: reference dimensions supplying driver values. +- **Target context**: booking account + write mode. +- **Allocation rules**: pick Source, Driver, Target dimensions and hierarchies; add rules row-by-row. + +## Methods +- Proportional (driver-based), Equal, Fixed Percentage, or Direct assignment via rules. +- Supports booking only to leaf targets; ensure hierarchies selected. + +## Good practices +- Keep drivers at same grain as target; avoid non-leaf booking errors. +- Use parameters for source period/version and target slice to reuse step. +- Test with small scope before full run; monitor job messages for unmapped members. + +Sources: 015-allocation-overview.md, 019-allocation-steps.md (SAC Help 2025.23). diff --git a/references/analytics-designer-planning.md b/references/analytics-designer-planning.md new file mode 100644 index 0000000..1ba1669 --- /dev/null +++ b/references/analytics-designer-planning.md @@ -0,0 +1,905 @@ +# SAP Analytics Cloud - Analytics Designer Planning Reference + +**Source**: SAP Analytics Designer Planning Documentation (Planning.pdf) +**Last Updated**: 2025-11-23 + +--- + +## Table of Contents + +1. [Overview](#overview) +2. [Getting the Planning Object](#getting-the-planning-object) +3. [Enabling and Disabling Planning](#enabling-and-disabling-planning) +4. [Refreshing Data](#refreshing-data) +5. [User Input and Data Entry](#user-input-and-data-entry) +6. [Planning Versions](#planning-versions) +7. [Version Management Operations](#version-management-operations) +8. [Version Copying](#version-copying) +9. [Data Locking](#data-locking) +10. [Planning Events](#planning-events) +11. [Members on the Fly](#members-on-the-fly) +12. [Planning Categories](#planning-categories) + +--- + +## Overview + +Analytics Designer reuses the Planning features of SAP Analytics Cloud and leverages capabilities through flexible scripting for customizing applications according to user requirements. Planning Data Models, Allocations, Data Action Triggers, and all Planning features can be integrated into applications. + +### What Analytics Designer CAN Do + +- Integrate Planning Data Models +- Trigger Allocations via scripting +- Execute Data Actions +- Manage Versions programmatically +- Control Data Locking via API +- Create/Update/Delete dimension members dynamically + +### What Analytics Designer CANNOT Do + +- Use Input Tasks +- Planning scripting for models based on BPC Write-Back + +--- + +## Getting the Planning Object + +Access planning functionality through the Table widget's `getPlanning()` method. + +### Syntax + +```javascript +Table.getPlanning(): Planning | undefined +``` + +### Return Value + +- Returns `Planning` object if the table has a planning model assigned +- Returns `undefined` if no planning model is assigned + +### Example + +```javascript +var planning = Table_1.getPlanning(); +if (planning) { + console.log("Planning model is assigned"); +} else { + console.log("No planning model assigned to this table"); +} +``` + +--- + +## Enabling and Disabling Planning + +Planning can be enabled/disabled both at design time and runtime. + +### Design Time Configuration + +In the Table's Builder panel, under **Properties** section: +- **Planning enabled**: Checkbox to enable planning capabilities +- **Mass Data Entry as default**: Optional setting for bulk data entry + +### Runtime Scripting + +#### Enable/Disable Planning + +```javascript +setEnabled(boolean): void +``` + +**Use Case**: Disable planning based on business rules (e.g., no budget changes in Q4). + +```javascript +// Disable planning if current date is in Q4 +var currentMonth = new Date().getMonth() + 1; +if (currentMonth >= 10) { + Table_Budget.getPlanning().setEnabled(false); + Application.showMessage("Budget changes are locked in Q4"); +} +``` + +#### Check Planning Status + +```javascript +isEnabled(): boolean +``` + +**Example**: + +```javascript +var planning = Table_1.getPlanning(); +if (planning && planning.isEnabled()) { + // Proceed with planning operations + performPlanningUpdate(); +} else { + Application.showMessage("Planning is not enabled"); +} +``` + +### Unbooked Data Configuration + +In the Table's Builder panel, configure **Unbooked Data** per dimension to show dimension members without existing data. Useful when: +- Planning model has no booked data yet +- End users need to see available members for planning + +--- + +## Refreshing Data + +The `refreshData()` method refreshes all data models and widgets in the application. + +### Syntax + +```javascript +Application.refreshData(): void +``` + +### When to Use + +- After data model updates via Live Connectivity +- When background process finishes updating master data +- After application is reopened +- After external data changes + +### Example + +```javascript +// Refresh button onClick event +function onRefreshClick() { + Application.showBusyIndicator(); + Application.refreshData(); + Application.hideBusyIndicator(); + Application.showMessage("Data refreshed successfully"); +} +``` + +--- + +## User Input and Data Entry + +### Setting User Input + +Update cell values programmatically using `setUserInput()`. + +```javascript +setUserInput(selectedData: Selection, value: String): boolean +``` + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `selectedData` | Selection | Cell selection from table | +| `value` | String | Value to set (max 17 characters) | + +### Return Value + +- `true`: Value was set successfully +- `false`: Value could not be set (locked cell, invalid value) + +### Value Format Rules + +| Format Type | Example | Description | +|-------------|---------|-------------| +| Raw Value | `"1234.567"` | Direct numeric value (uses user's formatting settings) | +| Scale Factor | `"*0.5"` | Multiply current value by factor | +| Scale Factor | `"*2"` | Double the current value | + +### Scaling Examples (Million Scale) + +| Input | Raw Value | Formatted Display | +|-------|-----------|-------------------| +| `"12345678"` | 12345678 | "12.35" | +| `"123456789"` | 123456789 | "123.46" | +| `"*0.5"` of 123456789 | 61728394.5 | "61.73" | + +### Constraints + +- Maximum value length: **17 characters** +- If value is scaled: **less than 7 digits** +- Can be performed from widget events or table events + +### Data Validation Behavior + +| Scenario | API Return | User Feedback | +|----------|------------|---------------| +| Invalid value | `false` | Error/warning message shown | +| Same value planned twice | `true` | Value is set | +| Cell is locked | `false` | Warning message shown | + +### Example: Custom Input Form + +```javascript +// Save Data button onClick +function onSaveData() { + var selection = Table_1.getSelections()[0]; + var newValue = InputField_Value.getValue(); + + if (!selection) { + Application.showMessage("Please select a cell first"); + return; + } + + var success = Table_1.getPlanning().setUserInput(selection, newValue); + + if (success) { + Table_1.getPlanning().submitData(); + Application.showMessage("Value saved successfully"); + } else { + Application.showMessage("Failed to save value - check if cell is locked"); + } +} +``` + +### Submitting Data + +Submit all pending changes to the planning model. + +```javascript +submitData(): boolean +``` + +**Important**: Always call `submitData()` after `setUserInput()` to persist changes. + +```javascript +// Submit all changes +Table_1.getPlanning().submitData(); +``` + +--- + +## Planning Versions + +SAP Analytics Cloud supports two types of planning versions: Private and Public. + +### Private Versions + +Private versions are only visible to the user who created them and other SAP Analytics Cloud solutions cannot see this data. + +#### Get All Private Versions + +```javascript +getPrivateVersions(): [Array of Planning Private Versions] | empty array +``` + +#### Get Specific Private Version + +```javascript +getPrivateVersion(versionId: String): Planning Private Version | undefined +``` + +#### Get Owner ID + +Returns the user ID of the user who created the private version. + +```javascript +getOwnerID(): String +``` + +### Public Versions + +Public versions are visible to all users and all solutions of SAP Analytics Cloud. + +#### Get All Public Versions + +```javascript +getPublicVersions(): [Array of Planning Public Versions] | empty array +``` + +#### Get Specific Public Version + +```javascript +getPublicVersion(versionId: String): Planning Public Version | undefined +``` + +### Version Common Methods + +Both version types support: + +```javascript +// Get internal ID (for getData() calls) +getId(): String + +// Get display ID (for UI dropdowns/texts) +getDisplayId(): String + +// Delete version (all versions except 'Actual' can be deleted) +deleteVersion(): boolean +``` + +### Example: List All Versions + +```javascript +function listAllVersions() { + var planning = Table_1.getPlanning(); + + console.log("=== Private Versions ==="); + var privateVersions = planning.getPrivateVersions(); + for (var i = 0; i < privateVersions.length; i++) { + var pv = privateVersions[i]; + console.log(pv.getId() + " - Owner: " + pv.getOwnerID()); + } + + console.log("=== Public Versions ==="); + var publicVersions = planning.getPublicVersions(); + for (var j = 0; j < publicVersions.length; j++) { + var pub = publicVersions[j]; + console.log(pub.getId() + " (" + pub.getDisplayId() + ")"); + } +} +``` + +--- + +## Version Management Operations + +### Automatic Save Behavior + +Any change in data in any type of version is **automatically saved**. Even if the browser is closed unexpectedly, data will still be present when the application is reopened by the same user who changed the data. + +### Publishing and Reverting + +To make private data visible to other users, publish the public version. + +#### Check for Unpublished Changes (Dirty Check) + +```javascript +isDirty(): boolean +``` + +**Visual Indicator**: Dirty versions show an asterisk (*) after the version name. + +```javascript +// Check before showing publish dialog +var version = Table_1.getPlanning().getPublicVersion("Forecast"); +if (version.isDirty()) { + // Show publish option + Button_Publish.setEnabled(true); +} else { + Application.showMessage("No changes to publish"); +} +``` + +#### Revert Changes + +Discard all data changes. + +```javascript +revert(): boolean +``` + +#### Publish Changes + +Make changes visible to other users. + +```javascript +publish(): boolean +``` + +### Success/Failure Messages + +After executing `publish()` or `revert()`: + +| Scenario | Message | +|----------|---------| +| Success | "Version has been published successfully" | +| No Changes | "You can't publish or revert version 'X' because you have not modified it" | + +### Publishing Private Versions + +Private versions have two publishing options: + +#### Option 1: Publish to Original Source + +```javascript +publish(): boolean +``` + +#### Option 2: Publish As New Public Version + +```javascript +publishAs(newVersionName: String, versionCategory: PlanningCategory): boolean +``` + +**Parameters**: +- `newVersionName`: Name for the new public version +- `versionCategory`: Category for the new version (see Planning Categories) + +### Example: Version Publish Dialog in Popup + +```javascript +// Useful when planning table is in a popup +// (toolbar is in background Canvas) + +function onPublishButtonClick() { + var planning = Table_1.getPlanning(); + var privateVersion = planning.getPrivateVersion(); + + if (!privateVersion) { + Application.showMessage("No private version available"); + return; + } + + if (!privateVersion.isDirty()) { + Application.showMessage("No changes to publish"); + return; + } + + Application.showBusyIndicator(); + + var success = privateVersion.publish(); + + Application.hideBusyIndicator(); + + if (success) { + Application.showMessage("Published successfully"); + Popup_Planning.close(); + } else { + Application.showMessage("Publish failed"); + } +} +``` + +### Publish and Leave Dialog + +SAP Analytics Cloud reminds users to publish data changes before leaving the application. + +**To disable this dialog**: +1. Go to Canvas **Styling** panel at design time +2. Under **Planning Settings** +3. Deselect "Remind of publishing all data changes before leaving" + +--- + +## Version Copying + +Create copies of versions using the `copy()` method. + +### Syntax + +```javascript +copy(newVersionName: string, planningCopyOption: PlanningCopyOption, versionCategory?: PlanningCategory): boolean +``` + +### PlanningCopyOptions Enumeration + +| Option | Description | +|--------|-------------| +| `PlanningCopyOptions.NoData` | Create a new empty version | +| `PlanningCopyOptions.AllData` | Copy all data from the source version | +| `PlanningCopyOptions.PlanningArea` | Copy only planning area data from source | + +### Example: Copy Version + +```javascript +function copyActualToBudget() { + var planning = Table_1.getPlanning(); + var actualVersion = planning.getPublicVersion("Actual"); + + if (actualVersion) { + var success = actualVersion.copy( + "Budget_2025", + PlanningCopyOptions.AllData, + PlanningCategory.Budget + ); + + if (success) { + Application.showMessage("Budget version created"); + Application.refreshData(); + } + } +} +``` + +--- + +## Data Locking + +The Data Locking script API allows checking and setting lock states even if the table isn't planning-enabled. + +### Data Locking API Methods + +```javascript +// Get data locking object +Table.getPlanning().getDataLocking() + +// Get lock state for selection +Table.getPlanning().getDataLocking().getState(selection) + +// Set lock state for selection +Table.getPlanning().getDataLocking().setState(selection, state) +``` + +### Checking Data Locking Enabled + +```javascript +var planning = Table_1.getPlanning(); +var dataLocking = planning.getDataLocking(); + +if (dataLocking) { + console.log("Data locking is enabled on this model"); +} else { + console.log("Data locking is not enabled"); +} +``` + +**Note**: You can also verify data locking in SAP Analytics Cloud Model Preferences. + +### Getting Lock State + +```javascript +getState(selection): DataLockingState | undefined +``` + +**Returns**: One of the `DataLockingState` values or `undefined` if state cannot be determined. + +#### DataLockingState Enumeration + +| State | Description | +|-------|-------------| +| `DataLockingState.Open` | Data can be edited by anyone | +| `DataLockingState.Restricted` | Only owner can edit | +| `DataLockingState.Locked` | No edits allowed by anyone | +| `DataLockingState.Mixed` | Selection contains multiple lock states | + +#### Undefined Return Conditions + +`getState()` returns `undefined` when: +- Selection is invalid +- Cell referenced by selection isn't found +- Cell is in unknown state +- Cell was created using "Add Calculation" at runtime + +### Example: Get Lock State + +```javascript +var selection = Table_1.getSelections()[0]; +var dataLocking = Table_1.getPlanning().getDataLocking(); + +var lockState = dataLocking.getState(selection); + +switch (lockState) { + case DataLockingState.Open: + console.log("Cell is open for editing"); + break; + case DataLockingState.Restricted: + console.log("Only owner can edit"); + break; + case DataLockingState.Locked: + console.log("Cell is fully locked"); + break; + case DataLockingState.Mixed: + console.log("Selection has mixed lock states"); + break; + default: + console.log("Lock state unknown"); +} +``` + +### Setting Lock State + +```javascript +setState(selection, state): boolean +``` + +**Returns**: `true` if successful, `false` otherwise. + +#### Restrictions + +- **Cannot set on private versions**: Error message: "You can only set data locks on public versions. Please use a public version and try again." +- **Cannot set Mixed state**: Error message: "You can't set the state with the value 'mixed'. Please specify either 'open', 'restricted' or 'locked' as value." +- **Multiple cell selection**: Lock state is applied to first selection only + +#### Valid States to Set + +- `DataLockingState.Open` +- `DataLockingState.Restricted` +- `DataLockingState.Locked` + +### Example: Set Lock State + +```javascript +var selection = Table_1.getSelections()[0]; +var dataLocking = Table_1.getPlanning().getDataLocking(); + +var success = dataLocking.setState(selection, DataLockingState.Locked); + +if (success) { + Application.showMessage("Data locked successfully"); +} else { + Application.showMessage("Failed to lock data"); +} +``` + +### Show Locks Option + +If **Show Locks** option is activated for the table, lock icons update automatically after `getState()` or `setState()` completes. + +### Important Notes + +- Disabling data locking on a model **deletes all locks** +- Re-enabling data locking resets all members to **default locking state** +- Same behavior if default locking state or driving dimensions are changed + +--- + +## Planning Events + +Two planning-related widgets provide `onBeforeExecute` events. + +### BpcPlanningSequence Widget + +```javascript +onBeforeExecute(): boolean +``` + +Called when user clicks the BPC planning sequence trigger. + +| Return Value | Behavior | +|--------------|----------| +| `true` or no value | BPC planning sequence executes | +| `false` | BPC planning sequence is ignored | + +### DataActionTrigger Widget + +```javascript +onBeforeExecute(): boolean +``` + +Called when user clicks the data action trigger. + +| Return Value | Behavior | +|--------------|----------| +| `true` or no value | Data action executes | +| `false` | Data action is ignored | + +### Example: Conditional Data Action Execution + +```javascript +// DataActionTrigger onBeforeExecute event +function onBeforeExecuteDataAction() { + // Check if user has confirmed + if (!userConfirmed) { + Application.showMessage("Please confirm before executing"); + return false; // Prevent execution + } + + // Check data locks + var selection = Table_1.getSelections()[0]; + var lockState = Table_1.getPlanning().getDataLocking().getState(selection); + + if (lockState === DataLockingState.Locked) { + Application.showMessage("Cannot execute - data is locked"); + return false; // Prevent execution + } + + return true; // Allow execution +} +``` + +--- + +## Members on the Fly + +The PlanningModel script API allows adding, updating, retrieving, and deleting dimension members dynamically. + +### Create Members + +```javascript +PlanningModel_1.createMembers(dimensionId, memberData) +``` + +**Example**: + +```javascript +PlanningModel_1.createMembers("LOCATION", { + id: "BERLIN", + description: "Berlin" +}); +``` + +**Note**: Creating multiple members with same ID results in an error. + +### Update Members + +```javascript +PlanningModel_1.updateMembers(dimensionId, memberData) +``` + +**Example** (adding data locking owner): + +```javascript +PlanningModel_1.updateMembers("LOCATION", { + id: "BERLIN", + dataLockingOwners: [{ + id: "ADMIN", + type: UserType.User + }] +}); +``` + +### Get Single Member + +```javascript +var member = PlanningModel_1.getMember(dimensionId, memberId); +console.log(member.description); +``` + +### Get Multiple Members (with Pagination) + +```javascript +var members = PlanningModel_1.getMembers(dimensionId, options); +``` + +**Example** (get members 5-12): + +```javascript +var members = PlanningModel_1.getMembers("LOCATION", { + offset: "4", // 0-indexed, so 4 = 5th member + limit: "8" // Return 8 members +}); +``` + +### Member Property Access Rights + +Certain member properties require specific rights/access on the dimension: + +| Property | Required Right/Access | +|----------|----------------------| +| `dataLockingOwner` | Data Locking Ownership | +| `responsible` | Responsible | +| `readers` | Data Access Control | +| `writers` | Data Access Control | + +### Important Notes + +#### Dimension Type Restriction + +Members can only be added to dimensions of type **"Generic"**. Adding members to these types is NOT supported: +- Account +- Version +- Time +- Organization + +#### Refresh After Changes + +After adding, updating, or deleting members, call refresh: + +```javascript +DataSource.refreshData() +// or +Application.refreshData() +``` + +This is required for charts/tables to reflect modified members in subsequent calls to: +- `DataSource.getPlanning().getState()` +- `DataSource.getPlanning().setState()` +- `DataSource.getData()` +- `Planning.setUserInput()` + +#### Large Model Considerations + +After adding members to very large models (millions of members), not all members may display immediately after refresh due to asynchronous background operations. Repeat the refresh after a short delay. + +#### Custom Property Naming + +When adding custom properties to planning members, use a **prefix** to avoid name conflicts with existing properties. + +### Complete Member Management Example + +```javascript +// Create new cost center +function createCostCenter(id, description, region) { + Application.showBusyIndicator(); + + try { + PlanningModel_1.createMembers("CostCenter", { + id: id, + description: description, + properties: { + "CUSTOM_Region": region, + "CUSTOM_Status": "Active" + }, + dataLockingOwners: [{ + id: Application.getUserInfo().userId, + type: UserType.User + }] + }); + + // Refresh to see new member + Application.refreshData(); + + Application.showMessage("Cost center created: " + id); + } catch (e) { + Application.showMessage("Error: " + e.message); + } finally { + Application.hideBusyIndicator(); + } +} +``` + +--- + +## Planning Categories + +Version categories classify versions in planning models. + +### PlanningCategory Enumeration + +| Category | Description | +|----------|-------------| +| `PlanningCategory.Actual` | Historical/actual data (auto-created, cannot be deleted) | +| `PlanningCategory.Planning` | General planning version | +| `PlanningCategory.Budget` | Budget version | +| `PlanningCategory.Forecast` | Forecast version | +| `PlanningCategory.RollingForecast` | Rolling forecast version | + +### Usage in Version Operations + +```javascript +// Create new version with specific category +privateVersion.publishAs("Forecast_Q4", PlanningCategory.Forecast); + +// Copy with category +sourceVersion.copy("Budget_2025", PlanningCopyOptions.AllData, PlanningCategory.Budget); +``` + +--- + +## Quick Reference Cheat Sheet + +```javascript +// === PLANNING OBJECT === +var planning = Table_1.getPlanning(); // Returns Planning | undefined +planning.isEnabled(); // Check if planning enabled +planning.setEnabled(true); // Enable/disable planning + +// === DATA ENTRY === +planning.setUserInput(selection, "100"); // Set absolute value +planning.setUserInput(selection, "*1.1"); // Increase by 10% +planning.submitData(); // Submit changes + +// === REFRESH === +Application.refreshData(); // Refresh all data sources + +// === VERSIONS === +planning.getPublicVersions(); // All public versions +planning.getPublicVersion("Budget"); // Specific public version +planning.getPrivateVersions(); // User's private versions +planning.getPrivateVersion("MyDraft"); // Specific private version + +// === VERSION OPERATIONS === +version.getId(); // Internal ID +version.getDisplayId(); // Display ID +version.isDirty(); // Has unsaved changes? +version.publish(); // Publish to source +version.publishAs("NewVer", PlanningCategory.Budget); // Publish as new +version.revert(); // Discard changes +version.deleteVersion(); // Delete version +version.copy("Copy", PlanningCopyOptions.AllData); // Copy version + +// === DATA LOCKING === +var locking = planning.getDataLocking(); +locking.getState(selection); // Open, Restricted, Locked, Mixed +locking.setState(selection, DataLockingState.Locked); + +// === MEMBERS ON THE FLY === +PlanningModel_1.createMembers("Dim", {id: "X", description: "Y"}); +PlanningModel_1.updateMembers("Dim", {id: "X", description: "Z"}); +PlanningModel_1.getMember("Dim", "X"); +PlanningModel_1.getMembers("Dim", {offset: "0", limit: "100"}); + +// === ENUMERATIONS === +// DataLockingState: Open, Restricted, Locked, Mixed +// PlanningCopyOptions: NoData, AllData, PlanningArea +// PlanningCategory: Actual, Planning, Budget, Forecast, RollingForecast +// UserType: User +``` + +--- + +**Documentation Links**: +- Analytics Designer API Reference: [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/2025.23/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/2025.23/en-US/index.html) +- Planning Overview: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/cd897576c3344475a208c2f7a52f151e.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/cd897576c3344475a208c2f7a52f151e.html) +- Data Locking: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/e07d46e950794d5a928a9b16d1394de6.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/18850a0e13944f53aa8a8b7c094ea29e/e07d46e950794d5a928a9b16d1394de6.html) diff --git a/references/api-reference.md b/references/api-reference.md new file mode 100644 index 0000000..57d4c91 --- /dev/null +++ b/references/api-reference.md @@ -0,0 +1,1054 @@ +# SAP Analytics Cloud - Analytics Designer API Reference for Planning + +**Source**: [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/2025.23/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/2025.23/en-US/index.html) +**Version**: 2025.23 +**Last Updated**: 2025-11-22 + +--- + +## Table of Contents + +1. [Application Class](#application-class) +2. [DataSource API](#datasource-api) +3. [Planning API (getPlanning)](#planning-api) +4. [PlanningModel API](#planningmodel-api) +5. [Data Actions API](#data-actions-api) +6. [Multi Actions API](#multi-actions-api) +7. [Calendar Integration API](#calendar-integration-api) +8. [BPC Planning Sequence API](#bpc-planning-sequence-api) +9. [Widget APIs](#widget-apis) +10. [Advanced Analytics APIs](#advanced-analytics-apis) +11. [Export APIs](#export-apis) +12. [Utility Classes](#utility-classes) +13. [Data Types and Structures](#data-types-and-structures) +14. [Enumerations](#enumerations) + +--- + +## Application Class + +The Application object provides access to global application functionality. + +### Properties & Methods + +| Method | Description | Return Type | +|--------|-------------|-------------| +| `getInfo()` | Get application information | ApplicationInfo | +| `getMode()` | Get current mode (View, Present, Embed) | ApplicationMode | +| `getTheme()` | Get current theme | Theme | +| `setTheme(theme)` | Set application theme | void | +| `getWidgets()` | Get all widgets in application | Widget[] | +| `getUserInfo()` | Get current user information | UserInfo | +| `getTeamsInfo()` | Get user's team information | TeamInfo[] | +| `getRolesInfo()` | Get user's role information | RoleInfo[] | +| `showBusyIndicator()` | Show loading indicator | void | +| `hideBusyIndicator()` | Hide loading indicator | void | +| `setAutomaticBusyIndicatorEnabled(enabled)` | Auto busy indicator | void | +| `refreshData()` | Refresh all data sources | void | +| `showMessage(text, type)` | Show toast message | void | +| `sendNotification(options)` | Send notification to users | void | +| `postMessage(message)` | Post message for embedding | void | +| `setCssClass(className)` | Set CSS class on application | void | +| `setCommentModeEnabled(enabled)` | Enable/disable comment mode | void | + +### Event Handlers + +| Event | Trigger Condition | +|-------|-------------------| +| `onInitialization` | Application loads | +| `onResize` | Browser window resizes | +| `onOrientationChange` | Device orientation changes | +| `onTimeout` | Session timeout warning | +| `onBeforeExecute` | Before data action executes | +| `onPostMessageReceived` | Message received from parent frame | +| `onShake` | Device shake detected (mobile) | + +### NotificationOptions Object + +```javascript +{ + title: string, + content: string, + receivers: string[], // User IDs + isSendEmail: boolean, + isSendMobileNotification: boolean, + parameters: object +} +``` + +### Example: Application Initialization + +```javascript +// onInitialization event +Application.showBusyIndicator(); + +// Get user and team info +var userInfo = Application.getUserInfo(); +var teams = Application.getTeamsInfo(); +console.log("User: " + userInfo.displayName); +console.log("Teams: " + teams.length); + +// Initialize default filters +var currentYear = new Date().getFullYear().toString(); +Table_1.getDataSource().setDimensionFilter("Year", currentYear); + +Application.hideBusyIndicator(); +``` + +--- + +## DataSource API + +Access data source functionality for querying and filtering. + +### Core Methods + +| Method | Description | Parameters | +|--------|-------------|------------| +| `getDimensions()` | Get all dimensions | none | +| `getHierarchies(dimId)` | Get dimension hierarchies | dimId: string | +| `getMeasures()` | Get all measures | none | +| `getMembers(dimId, options)` | Get dimension members | dimId: string, options: MemberOptions | +| `getMember(dimId, memberId)` | Get single member | dimId: string, memberId: string | +| `getData()` | Get data from source | none | +| `getResultSet()` | Get result set | none | +| `getDataSelections()` | Get current data selections | none | +| `isResultEmpty()` | Check if result is empty | none | +| `setDimensionFilter(dimId, filter)` | Set filter on dimension | dimId: string, filter: string/MemberInfo[] | +| `removeDimensionFilter(dimId)` | Remove dimension filter | dimId: string | +| `getDimensionFilters(dimId)` | Get active filters | dimId: string | +| `setVariableValue(varId, value)` | Set variable value | varId: string, value: string | +| `getVariables()` | Get all variables | none | +| `getVariableValues(varId)` | Get variable values | varId: string | +| `removeVariableValue(varId)` | Remove variable value | varId: string | +| `setHierarchy(dimId, hierarchyId)` | Set dimension hierarchy | dimId: string, hierarchyId: string | +| `getHierarchyLevel(dimId)` | Get current hierarchy level | dimId: string | +| `expandNode(node)` | Expand hierarchy node | node: MemberInfo | +| `collapseNode(node)` | Collapse hierarchy node | node: MemberInfo | +| `refreshData()` | Refresh data source | none | +| `setRefreshPaused(paused)` | Pause/resume auto refresh | paused: boolean | +| `getRefreshPaused()` | Check if refresh paused | none | + +### MemberOptions Object + +```javascript +{ + limit: number, // Max members to return + offset: number, // Skip first N members + accessMode: MemberAccessMode, // MasterData, BookedValues + search: string, // Search string + hierarchyId: string // Specific hierarchy +} +``` + +### Filter Syntax (MDX) + +```javascript +// Single member +"[Version].[parentId].&[public.Actual]" + +// Multiple members using array +var members = ["2024", "2025"]; +Table_1.getDataSource().setDimensionFilter("Year", members); + +// Using MemberInfo objects +var memberInfos = [ + {id: "2024", description: "Year 2024"}, + {id: "2025", description: "Year 2025"} +]; +Table_1.getDataSource().setDimensionFilter("Year", memberInfos); +``` + +--- + +## Planning API + +Access planning functionality on tables via `getPlanning()`. + +### Methods + +| Method | Description | Return Type | +|--------|-------------|-------------| +| `isEnabled()` | Check if planning enabled | boolean | +| `setEnabled(enabled)` | Enable/disable planning at runtime | void | +| `getPublicVersions()` | Get all public versions | PlanningPublicVersion[] | +| `getPublicVersion(versionId)` | Get specific public version | PlanningPublicVersion | +| `getPrivateVersion()` | Get current private version | PlanningPrivateVersion | +| `getPrivateVersions()` | Get all user's private versions | PlanningPrivateVersion[] | +| `getPlanningAreaInfo()` | Get planning area information | PlanningAreaInfo | +| `setUserInput(selection, value)` | Set cell value (max 17 chars, use "*" prefix for scaling) | boolean | +| `submitData()` | Submit pending changes | boolean | +| `getDataLocking()` | Get data locking object | DataLocking | + +### setUserInput Value Formatting + +| Format | Example | Description | +|--------|---------|-------------| +| Raw value | `"1234.567"` | Direct value using user formatting | +| Scale factor | `"*0.5"` | Multiply existing value by 0.5 | +| Scale factor | `"*2"` | Double the existing value | + +**Constraints**: Max 17 characters; scaled values max 7 digits. + +### PlanningPublicVersion Object + +| Property/Method | Description | +|----------------|-------------| +| `id` | Version identifier | +| `description` | Version description | +| `getId()` | Get internal ID (for getData() calls) | +| `getDisplayId()` | Get display ID (for UI dropdowns/texts) | +| `isDirty()` | Check for unsaved changes | +| `startEditMode()` | Start editing (creates private copy) | +| `revert()` | Revert unpublished changes (returns boolean) | +| `publish()` | Publish changes to public (returns boolean) | +| `deleteVersion()` | Delete version (returns boolean, all except 'Actual') | +| `copy(name, option, category?)` | Create private copy of version | + +### PlanningPrivateVersion Object + +| Property/Method | Description | +|----------------|-------------| +| `id` | Version identifier | +| `description` | Version description | +| `getId()` | Get internal ID | +| `getDisplayId()` | Get display ID | +| `getOwnerID()` | Get user ID of version creator | +| `isDirty()` | Check for unsaved changes | +| `publish()` | Publish to source public version (returns boolean) | +| `publishAs(newName, category)` | Publish as new public version (returns boolean) | +| `revert()` | Discard private version (returns boolean) | + +### Version Copy Method + +```javascript +copy(newVersionName: string, planningCopyOption: PlanningCopyOption, + versionCategory?: PlanningCategory): boolean +``` + +### PlanningCopyOptions Enumeration + +| Value | Description | +|-------|-------------| +| `PlanningCopyOptions.NoData` | Create new empty version | +| `PlanningCopyOptions.AllData` | Copy all data from source | +| `PlanningCopyOptions.PlanningArea` | Copy only planning area data | + +### PlanningCategory Enumeration + +| Value | Description | +|-------|-------------| +| `PlanningCategory.Actual` | Historical/actual data (auto-created) | +| `PlanningCategory.Planning` | General planning version | +| `PlanningCategory.Budget` | Budget version | +| `PlanningCategory.Forecast` | Forecast version | +| `PlanningCategory.RollingForecast` | Rolling forecast version | + +### DataLocking Object + +| Method | Description | +|--------|-------------| +| `getState(selection)` | Get lock state for cells (returns DataLockingState or undefined) | +| `setState(selection, state)` | Set lock state on public versions only (returns boolean) | +| `getOwners(selection)` | Get lock owners | + +**getState() Returns undefined when**: +- Selection is invalid +- Cell referenced by selection isn't found +- Cell is in unknown state +- Cell was created using "Add Calculation" at runtime + +**setState() Restrictions**: +- Cannot set on private versions +- Cannot set `DataLockingState.Mixed` +- Multiple selections: applies to first selection only + +### DataLockingState Enumeration + +| Value | Description | +|-------|-------------| +| `DataLockingState.Open` | Data can be edited by anyone | +| `DataLockingState.Restricted` | Only owner can edit | +| `DataLockingState.Locked` | No edits allowed | +| `DataLockingState.Mixed` | Mixed states in selection (read-only, cannot set) | + +### Example: Version Management + +```javascript +// Get all public versions +var versions = Table_1.getPlanning().getPublicVersions(); + +for (var i = 0; i < versions.length; i++) { + console.log(versions[i].id + ": " + versions[i].description); +} + +// Start edit mode on public version +var budget = Table_1.getPlanning().getPublicVersion("Budget_2025"); +budget.startEditMode(); + +// Work with private version +var privateVer = Table_1.getPlanning().getPrivateVersion(); +if (privateVer && privateVer.isDirty()) { + privateVer.publish(); + Application.showMessage("Changes published successfully"); +} +``` + +### Example: Data Entry with Lock Check + +```javascript +// Get current selection +var selection = Table_1.getSelections()[0]; + +// Check if locked +var dataLocking = Table_1.getPlanning().getDataLocking(); +var lockState = dataLocking.getState(selection); + +if (lockState === DataLockingState.Open) { + // Set value (use "*" prefix for factor multiplication) + Table_1.getPlanning().setUserInput(selection, "1000"); + Table_1.getPlanning().submitData(); +} else if (lockState === DataLockingState.Mixed) { + Application.showMessage("Selection contains mixed lock states"); +} else { + Application.showMessage("Data is locked or restricted"); +} +``` + +--- + +## PlanningModel API + +Access planning model for master data operations. + +### Methods + +| Method | Description | Parameters | +|--------|-------------|------------| +| `getMembers(dimId, options)` | Get dimension members | dimId: string, options: object | +| `getMember(dimId, memberId)` | Get single member | dimId: string, memberId: string | +| `createMembers(dimId, members)` | Create new members | dimId: string, members: MemberData[] | +| `updateMembers(dimId, members)` | Update existing members | dimId: string, members: MemberData[] | +| `deleteMembers(dimId, memberIds)` | Delete members | dimId: string, memberIds: string[] | + +### PlanningModelMember Object + +```javascript +{ + id: string, // Member ID (required) + description: string, // Display text + parentId: string, // Parent in hierarchy + hierarchies: object, // Hierarchy assignments + properties: object, // Custom properties + responsible: string[], // Responsible users + readers: string[], // Read access users + writers: string[], // Write access users + dataLockingOwners: string[] // Data locking owners +} +``` + +### getMembers() Options + +```javascript +var members = PlanningModel_1.getMembers("LOCATION", { + offset: "4", // 0-indexed (4 = 5th member) + limit: "8" // Return 8 members +}); +``` + +### Important Notes for Members on the Fly + +**Dimension Type Restriction**: Members can only be added to dimensions of type **"Generic"**. NOT supported for: +- Account +- Version +- Time +- Organization + +**Refresh Required**: After creating/updating/deleting members, call refresh for changes to appear in widgets: + +```javascript +DataSource.refreshData(); +// or +Application.refreshData(); +``` + +**Large Models**: After adding members to very large models (millions of members), repeat refresh after a short delay as operations run asynchronously. + +**Custom Property Naming**: Use a prefix for custom properties to avoid conflicts (e.g., `"CUSTOM_Region"`). + +**Property Access Rights**: + +| Property | Required Right/Access | +|----------|----------------------| +| `dataLockingOwner` | Data Locking Ownership | +| `responsible` | Responsible | +| `readers` | Data Access Control | +| `writers` | Data Access Control | + +### Example: Master Data Management + +```javascript +// Get all cost centers with properties +var costCenters = PlanningModel_1.getMembers("CostCenter", { + limit: 5000 +}); + +// Create new cost center with security +PlanningModel_1.createMembers("CostCenter", [{ + id: "CC_NEW_001", + description: "New Cost Center", + parentId: "CC_PARENT", + properties: { + "CUSTOM_Status": "Active", + "CUSTOM_Region": "EMEA" + }, + responsible: ["USER1"], + writers: ["USER1", "USER2"], + dataLockingOwners: [{ + id: "USER1", + type: UserType.User + }] +}]); + +// Refresh to see new member +Application.refreshData(); +``` + +### UserType Enumeration + +| Value | Description | +|-------|-------------| +| `UserType.User` | Individual user | + +--- + +## Data Actions API + +Execute data actions programmatically. + +### Methods + +| Method | Description | +|--------|-------------| +| `execute()` | Execute synchronously | +| `executeInBackground()` | Execute asynchronously | +| `setParameterValue(paramId, value)` | Set parameter value | +| `getParameterValue(paramId)` | Get parameter value | +| `isAllMembersSelected(paramId)` | Check if all members selected | +| `setAllMembersSelected(paramId, selected)` | Select/deselect all members | + +### Events + +| Event | Description | +|-------|-------------| +| `onExecutionStatusUpdate` | Status changed during execution | +| `onExecutionComplete` | Execution finished | + +### DataActionTrigger Widget Events + +| Event | Description | +|-------|-------------| +| `onBeforeExecute` | Called when user clicks trigger. Return `true` to execute, `false` to cancel | + +```javascript +// DataActionTrigger onBeforeExecute event +onBeforeExecute(): boolean { + // Check data locks before execution + var selection = Table_1.getSelections()[0]; + var lockState = Table_1.getPlanning().getDataLocking().getState(selection); + + if (lockState === DataLockingState.Locked) { + Application.showMessage("Cannot execute - data is locked"); + return false; // Cancel execution + } + return true; // Allow execution +} +``` + +--- + +## Multi Actions API + +Execute multi actions for orchestrated operations. + +### Methods + +| Method | Description | +|--------|-------------| +| `execute()` | Execute multi action | +| `executeInBackground()` | Execute asynchronously | +| `setParameterValue(paramId, value)` | Set cross-step parameter | +| `getParameterValue(paramId)` | Get parameter value | +| `isAllMembersSelected(paramId)` | Check if all members selected | +| `setAllMembersSelected(paramId, selected)` | Select/deselect all members | + +--- + +## Calendar Integration API + +Manage planning calendar tasks and processes programmatically. + +### CalendarIntegration + +Main entry point for calendar operations. + +| Method | Description | +|--------|-------------| +| `getProcess(processId)` | Get calendar process | +| `getTask(taskId)` | Get calendar task | +| `createGeneralTask(config)` | Create general task | +| `createReviewTask(config)` | Create review task | +| `createCompositeTask(config)` | Create composite task | + +### CalendarProcess + +| Method | Description | +|--------|-------------| +| `getId()` | Get process ID | +| `getName()` | Get process name | +| `getDescription()` | Get process description | +| `getStatus()` | Get process status | +| `getTasks()` | Get all tasks in process | +| `activate()` | Activate process | +| `complete()` | Mark process complete | + +### CalendarEvent (Base for Tasks) + +| Method | Description | +|--------|-------------| +| `getId()` | Get event ID | +| `getDescription()` | Get description | +| `getStatus()` | Get current status | +| `setProgress(progress)` | Set completion progress | +| `getProgress()` | Get completion progress | +| `getAssignees()` | Get assigned users | +| `getDueDate()` | Get due date | + +### CalendarCompositeTask + +Supports multi-reviewer approval workflows. + +| Method | Description | +|--------|-------------| +| `getReviewers()` | Get all reviewers | +| `addReviewer(userId)` | Add reviewer | +| `removeReviewer(userId)` | Remove reviewer | +| `submit()` | Submit for review | +| `approve()` | Approve task | +| `reject(comment)` | Reject with comment | + +### CalendarReviewTask + +| Method | Description | +|--------|-------------| +| `approve()` | Approve submission | +| `reject(comment)` | Reject with comment | +| `getReviewStatus()` | Get review status | + +--- + +## BPC Planning Sequence API + +Integrate with SAP BPC planning sequences. + +### BpcPlanningSequence + +| Method | Description | +|--------|-------------| +| `execute()` | Execute planning sequence | +| `openPromptDialog()` | Open variable prompt dialog | +| `getBpcPlanningSequenceDataSource()` | Get associated data source | +| `setParameterValue(paramId, value)` | Set parameter value | +| `getParameterValue(paramId)` | Get parameter value | + +### BpcPlanningSequence Events + +| Event | Description | +|-------|-------------| +| `onBeforeExecute` | Called when user clicks trigger. Return `true` to execute, `false` to cancel | + +```javascript +// onBeforeExecute event handler +onBeforeExecute(): boolean { + // Validate before execution + if (!isUserAuthorized()) { + Application.showMessage("Not authorized"); + return false; // Cancel execution + } + return true; // Allow execution +} +``` + +--- + +## Widget APIs + +### Table Widget + +| Method | Description | +|--------|-------------| +| `getPlanning()` | Get planning object | +| `getDataSource()` | Get data source | +| `getSelections()` | Get selected cells | +| `addDimensionToRows(dimId)` | Add dimension to rows | +| `addDimensionToColumns(dimId)` | Add dimension to columns | +| `removeDimension(dimId)` | Remove dimension | +| `setVisible(visible)` | Show/hide table | +| `setCompactDisplayEnabled(enabled)` | Enable compact display | +| `setZeroSuppressionEnabled(enabled)` | Enable zero suppression | +| `getNumberFormat()` | Get number format settings | +| `rankBy(measure, order, count)` | Apply ranking | +| `sortByValue(measure, order)` | Sort by measure | +| `sortByMember(dimId, order)` | Sort by member | +| `openNavigationPanel()` | Open navigation panel | +| `closeNavigationPanel()` | Close navigation panel | +| `getActiveDimensionProperties()` | Get active dimension properties | + +### Chart Widget + +| Method | Description | +|--------|-------------| +| `getDataSource()` | Get data source | +| `addDimension(dimId)` | Add dimension | +| `removeDimension(dimId)` | Remove dimension | +| `addMeasure(measureId)` | Add measure | +| `removeMeasure(measureId)` | Remove measure | +| `addMember(dimId, memberId)` | Add member to dimension | +| `removeMember(dimId, memberId)` | Remove member | +| `setVisible(visible)` | Show/hide chart | +| `getNumberFormat()` | Get number format | +| `setAxisScale(axis, scale)` | Set axis scale | +| `getEffectiveAxisScale(axis)` | Get effective axis scale | +| `getForecast()` | Get forecast data | +| `getDataChangeInsights()` | Get variance insights | +| `setQuickActionsVisibility(visible)` | Show/hide quick actions | +| `setContextMenuVisible(visible)` | Show/hide context menu | +| `openInNewStory()` | Open in new story | + +### GeoMap Widget + +| Method | Description | +|--------|-------------| +| `getLayer(layerId)` | Get map layer | +| `setContextMenuVisible(visible)` | Show/hide context menu | +| `setQuickActionsVisibility(visible)` | Show/hide quick actions | +| `openInNewStory()` | Open in new story | + +### Input Controls + +**Dropdown**: +| Method | Description | +|--------|-------------| +| `getSelectedKey()` | Get selected value | +| `setSelectedKey(key)` | Set selected value | +| `getSelectedText()` | Get selected text | +| `addItem(key, text)` | Add item | +| `removeItem(key)` | Remove item | +| `removeAllItems()` | Clear all items | + +**ListBox** (Multi-select): +| Method | Description | +|--------|-------------| +| `getSelectedKeys()` | Get selected values | +| `setSelectedKeys(keys)` | Set selected values | +| `getSelectedTexts()` | Get selected texts | + +**CheckboxGroup**: +| Method | Description | +|--------|-------------| +| `getSelectedKeys()` | Get checked items | +| `setSelectedKeys(keys)` | Set checked items | + +**RadioButtonGroup**: +| Method | Description | +|--------|-------------| +| `getSelectedKey()` | Get selected option | +| `setSelectedKey(key)` | Set selected option | + +**Slider/RangeSlider**: +| Method | Description | +|--------|-------------| +| `getValue()` / `getRange()` | Get current value | +| `setValue(val)` / `setRange(range)` | Set value | + +**Switch**: +| Method | Description | +|--------|-------------| +| `isOn()` | Check if switch is on | +| `setOn(on)` | Set switch state | + +### Container Widgets + +**Panel/FlowPanel**: +| Method | Description | +|--------|-------------| +| `showBusyIndicator()` | Show busy indicator | +| `hideBusyIndicator()` | Hide busy indicator | +| `moveWidget(widget, position)` | Move widget | + +**PageBook**: +| Method | Description | +|--------|-------------| +| `getPage(pageId)` | Get specific page | +| `setSelectedKey(pageId)` | Switch to page | +| `getSelectedKey()` | Get current page | + +**TabStrip**: +| Method | Description | +|--------|-------------| +| `getTab(tabId)` | Get specific tab | +| `setSelectedKey(tabId)` | Switch to tab | +| `getSelectedKey()` | Get current tab | + +**Popup**: +| Method | Description | +|--------|-------------| +| `open()` | Open popup | +| `close()` | Close popup | +| `setTitle(title)` | Set popup title | + +--- + +## Advanced Analytics APIs + +### DataChangeInsights + +Change detection and trend analysis. + +| Method | Description | +|--------|-------------| +| `compareApplicationStateWithSnapshot(date)` | Compare current with snapshot | +| `compareSnapshots(date1, date2)` | Compare two snapshots | +| `saveSnapshot()` | Save current state as snapshot | +| `listRecentSnapshotDates()` | List available snapshots | +| `openSubscriptionDialog()` | Open subscription dialog | +| `isRunBySnapshotGeneration()` | Check if run by snapshot | + +### SearchToInsight + +Natural language analytics. + +| Method | Description | +|--------|-------------| +| `openDialog()` | Open search dialog | +| `closeDialog()` | Close search dialog | +| `applySearchToChart(query, chart)` | Apply search to chart | + +**SearchToInsightMode**: `Simple`, `Advanced` + +### SmartDiscovery + +Automated insight generation. + +| Method | Description | +|--------|-------------| +| `setDimensionSettings(settings)` | Configure dimensions | +| `setMeasureSettings(settings)` | Configure measures | +| `setFilterSettings(settings)` | Configure filters | +| `setPanelVisible(visible)` | Show/hide panel | + +### Bookmarks + +**BookmarkSet**: +| Method | Description | +|--------|-------------| +| `save(name)` | Save new bookmark | +| `apply(bookmarkId)` | Apply bookmark | +| `getAll()` | Get all bookmarks | +| `deleteBookmark(bookmarkId)` | Delete bookmark | +| `getAppliedBookmark()` | Get currently applied | +| `isSameAsApplicationState(bookmarkId)` | Compare with current | + +--- + +## Export APIs + +### ExportPdf + +| Method | Description | +|--------|-------------| +| `setFileName(name)` | Set output file name | +| `setPageSize(size)` | Set page size (A4, A3, Letter, etc.) | +| `setPageOrientation(orientation)` | Portrait or Landscape | +| `setHeaderText(text)` | Set header text | +| `setFooterText(text)` | Set footer text | +| `exportReport()` | Export as PDF | +| `exportView()` | Export current view | + +### ExportExcel + +| Method | Description | +|--------|-------------| +| `setFileName(name)` | Set output file name | +| `isExportFormattedValues()` | Check if exporting formatted | +| `setIndentedHierarchy(enabled)` | Enable indented hierarchy | +| `exportReport()` | Export as Excel | + +### ExportCsv + +| Method | Description | +|--------|-------------| +| `setFileName(name)` | Set output file name | +| `setScope(scope)` | Set export scope | +| `isHierarchyLevelsInIndividualCells()` | Check hierarchy export | +| `exportReport()` | Export as CSV | + +### ExportPptx + +| Method | Description | +|--------|-------------| +| `setFileName(name)` | Set output file name | +| `setPageRange(range)` | Set pages to export | +| `isAppendixVisible()` | Check appendix setting | +| `exportView()` | Export as PowerPoint | + +--- + +## Utility Classes + +### ConvertUtils + +| Method | Description | +|--------|-------------| +| `stringToInteger(str)` | Convert string to integer | +| `stringToNumber(str)` | Convert string to float | +| `numberToString(num)` | Convert number to string | + +### ArrayUtils + +| Method | Description | +|--------|-------------| +| `create(type, size)` | Create typed array | + +### StringUtils + +| Method | Description | +|--------|-------------| +| `replaceAll(str, search, replace)` | Replace all occurrences | + +### NavigationUtils + +| Method | Description | +|--------|-------------| +| `openStory(storyId)` | Open another story | +| `createStoryUrl(storyId)` | Create story URL | +| `openApplication(appId)` | Open another application | +| `createApplicationUrl(appId)` | Create application URL | +| `openDataAnalyzer(config)` | Open Data Analyzer | +| `openUrl(url)` | Open external URL | + +### DateFormat + +```javascript +// Format date +var formatted = DateFormat.format(new Date(), "yyyy-MM-dd"); +``` + +### NumberFormat + +| Property | Values | +|----------|--------| +| `scaleUnit` | Thousand, Million, Billion, AutoFormatted, Unformatted | +| `scaleFormat` | Short, Long, Default | +| `signDisplay` | Default, MinusAsPrefix, MinusAsParentheses, PlusMinusAsPrefix | + +### TimeRange + +| Method | Description | +|--------|-------------| +| `create(start, end, granularity)` | Create time range | +| `createMonthRange(year, month)` | Create month range | +| `createWeekRange(year, week)` | Create week range | +| `createYearRange(year)` | Create year range | + +**Granularity**: Day, Month, Year, Hour, Minute, Second, Quarter, HalfYear, Millisecond + +--- + +## Data Types and Structures + +### DimensionInfo + +```javascript +{ + id: string, + description: string, + modelId: string +} +``` + +### MemberInfo + +```javascript +{ + id: string, + displayId: string, + description: string, + dimensionId: string, + properties: object +} +``` + +### ResultMemberInfo + +```javascript +{ + id: string, + parentId: string, + description: string, + properties: object +} +``` + +### DataCell + +```javascript +{ + rawValue: number, + formattedValue: string +} +``` + +### Filter Values + +**SingleFilterValue**: +```javascript +{ value: string, description: string, exclude: boolean } +``` + +**MultipleFilterValue**: +```javascript +{ values: string[], descriptions: string[], exclude: boolean } +``` + +**RangeFilterValue**: +```javascript +{ + from: any, to: any, + greater: boolean, greaterOrEqual: boolean, + less: boolean, lessOrEqual: boolean +} +``` + +--- + +## Enumerations + +### ApplicationMode +`View`, `Present`, `Embed` + +### ApplicationMessageType +`Info`, `Success`, `Warning`, `Error` + +### MemberAccessMode +`MasterData`, `BookedValues` + +### DataLockingState +`Open`, `Restricted`, `Locked`, `Mixed` + +### SortOrder +`Ascending`, `Descending`, `Default` + +### RankOrder +`Top`, `Bottom` + +### ForecastType +`Auto`, `None`, `TripleExponentialSmoothing` + +### ExportScope +`PointOfView`, `All` + +### PageOrientation +`Portrait`, `Landscape` + +### PageSize +`A4`, `A3`, `A5`, `Letter`, `Legal`, `Auto` + +### MemberDisplayMode +`DisplayId`, `Description`, `DisplayIdAndDescription` + +### Direction +`Horizontal`, `Vertical` + +### DeviceOrientation +`Angle0`, `Angle90Clockwise`, `Angle180`, `Angle90Counterclockwise` + +### LayoutUnit +`Pixel`, `Percent`, `Grid`, `Auto` + +--- + +## Quick Reference Cheat Sheet + +```javascript +// === APPLICATION === +Application.showBusyIndicator(); +Application.hideBusyIndicator(); +Application.showMessage("text", ApplicationMessageType.Success); +Application.getUserInfo(); +Application.getTeamsInfo(); +Application.sendNotification({title: "Alert", receivers: ["USER1"]}); + +// === DATA SOURCE === +Table_1.getDataSource().getDimensions(); +Table_1.getDataSource().getHierarchies("CostCenter"); +Table_1.getDataSource().getMembers("Dim", {accessMode: MemberAccessMode.BookedValues}); +Table_1.getDataSource().setDimensionFilter("Dim", "value"); +Table_1.getDataSource().removeDimensionFilter("Dim"); +Table_1.getDataSource().setRefreshPaused(true); + +// === PLANNING === +Table_1.getPlanning().isEnabled(); +Table_1.getPlanning().getPublicVersions(); +Table_1.getPlanning().getPrivateVersion(); +Table_1.getPlanning().getPlanningAreaInfo(); +Table_1.getPlanning().setUserInput(selection, "value"); +Table_1.getPlanning().submitData(); + +// === VERSION MANAGEMENT === +var publicVer = Table_1.getPlanning().getPublicVersion("Budget"); +publicVer.startEditMode(); +publicVer.isDirty(); +publicVer.publish(); + +var privateVer = Table_1.getPlanning().getPrivateVersion(); +privateVer.publish(); +privateVer.publishAs("Budget_v2", "Revised Budget"); + +// === DATA LOCKING === +var dataLocking = Table_1.getPlanning().getDataLocking(); +var state = dataLocking.getState(selection); +// States: Open, Restricted, Locked, Mixed +dataLocking.setState(selection, DataLockingState.Locked); + +// === PLANNING MODEL === +PlanningModel_1.getMembers("Dim"); +PlanningModel_1.createMembers("Dim", [{id: "X", description: "Y"}]); +PlanningModel_1.updateMembers("Dim", [{id: "X", description: "Z"}]); +PlanningModel_1.deleteMembers("Dim", ["X"]); + +// === DATA ACTIONS === +DataAction_1.setParameterValue("Param", "Value"); +DataAction_1.execute(); +DataAction_1.executeInBackground(); + +// === MULTI ACTIONS === +MultiAction_1.setParameterValue("Param", "Value"); +MultiAction_1.execute(); + +// === EXPORTS === +ExportPdf.setFileName("report"); +ExportPdf.setPageOrientation(PageOrientation.Landscape); +ExportPdf.exportReport(); + +// === CONVERSIONS === +ConvertUtils.stringToInteger("123"); +ConvertUtils.numberToString(123); +``` + +--- + +**Documentation Links**: +- Full API Reference (2025.23): [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/2025.23/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/2025.23/en-US/index.html) +- SAP Analytics Cloud Help: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD) +- SAP Community: [https://community.sap.com/topics/sap-analytics-cloud](https://community.sap.com/topics/sap-analytics-cloud) diff --git a/references/api-snippets.md b/references/api-snippets.md new file mode 100644 index 0000000..7f36da8 --- /dev/null +++ b/references/api-snippets.md @@ -0,0 +1,48 @@ +# Planning API Snippets (Analytics Designer) + +Use inside analytic applications for planning automation. Ensure widgets/models match IDs below. + +## getPlanning() — table planning ops +```javascript +// Is planning enabled? +const enabled = Table_1.getPlanning().isEnabled(); + +// Versions +const publics = Table_1.getPlanning().getPublicVersions(); +const priv = Table_1.getPlanning().getPrivateVersion(); + +// Write and submit +Table_1.getPlanning().setUserInput(selection, value); // selection: SelectionType object +Table_1.getPlanning().submitData(); +``` + +## PlanningModel API — master data +```javascript +// Read members with properties +const members = PlanningModel_1.getMembers("CostCenter"); + +// Create / update / delete +PlanningModel_1.createMembers("CostCenter", [{ id: "CC100", description: "Marketing" }]); +PlanningModel_1.updateMembers("CostCenter", [{ id: "CC100", description: "Marketing Dept" }]); +PlanningModel_1.deleteMembers("CostCenter", ["CC100"]); +``` + +## DataSource API — filtering +```javascript +// Filter a dimension +Table_1.getDataSource().setDimensionFilter( + "Version", + "[Version].[parentId].&[public.Actual]" +); + +// Members with booked values only +const booked = Table_1.getDataSource().getMembers( + "Account", + { accessMode: MemberAccessMode.BookedValues } +); + +// Remove filter +Table_1.getDataSource().removeDimensionFilter("Version"); +``` + +Notes: keep IDs consistent with widgets; submitData respects data locks/edit mode; filters impact prompts/parameters when using story filters as defaults. diff --git a/references/data-actions.md b/references/data-actions.md new file mode 100644 index 0000000..354c5e5 --- /dev/null +++ b/references/data-actions.md @@ -0,0 +1,520 @@ +# SAP Analytics Cloud - Data Actions and Multi Actions Reference + +**Source**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/69a370e6cfd84315973101389baacde0.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/69a370e6cfd84315973101389baacde0.html) +**Last Updated**: 2025-11-22 + +--- + +## Table of Contents + +1. [Data Actions Overview](#data-actions-overview) +2. [Data Action Step Types](#data-action-step-types) +3. [Multi Actions Overview](#multi-actions-overview) +4. [Multi Action Step Types](#multi-action-step-types) +5. [Parameters Configuration](#parameters-configuration) +6. [Execution Methods](#execution-methods) +7. [Performance Best Practices](#performance-best-practices) +8. [Troubleshooting](#troubleshooting) + +--- + +## Data Actions Overview + +Data actions perform calculations and data manipulation on planning models. All steps within a data action run on a single transaction - if any step fails, all changes roll back. + +### Key Characteristics + +- **Single Model**: Data actions operate on one target model +- **Single Version**: All steps target one version +- **Transaction**: All-or-nothing execution +- **Rollback**: Automatic rollback on failure + +### Creating a Data Action (020-create-data-action.md) +1. Files → Create New → Data Action; choose target planning model. +2. Add steps (copy/cross-copy/allocation/embedded/conversion/advanced formulas) in needed order. +3. Set parameters (member/number/measure); enable sorting if many prompts. +4. Choose write mode per step (append vs overwrite); validate mappings to leaf members. +5. Save, test on small slice, then schedule/run via starter or calendar. + +--- + +## Data Action Step Types + +### 1. Copy Step + +Copies data from source to target with dimension mapping. + +**Configuration**: +``` +Source Scope: + Version: Actual + Year: 2024 + Account: All + +Target Scope: + Version: Budget + Year: 2025 + Account: All + +Dimension Mapping: + Account → Account (same member) + CostCenter → CostCenter (same member) + Year: 2024 → 2025 (fixed mapping) +``` + +**Use Cases**: +- Copy actuals to forecast +- Seed budget from prior year +- Copy between models + +### 2. Advanced Formula Step + +Write custom formulas using SAP Analytics Cloud formula syntax. + +**Formula Syntax**: +``` +// Basic assignment +[Account].[Revenue] = 1000000 + +// Reference other members +[Account].[Gross_Profit] = [Account].[Revenue] - [Account].[COGS] + +// Conditional logic +IF [Account].[Revenue] > 0 THEN + [Account].[Margin] = [Account].[Gross_Profit] / [Account].[Revenue] +ELSE + [Account].[Margin] = 0 +ENDIF + +// Cross-version reference +[Version].[Forecast] = [Version].[Actual] * 1.05 + +// Time-based calculations +[Date].[2025.Q1] = [Date].[2024.Q1] * (1 + [Account].[Growth_Rate]) + +// RESULTLOOKUP for unmatched dimensions +[Account].[Allocation] = RESULTLOOKUP([Account].[Driver], [CostCenter].[TOTAL]) +``` + +**Operators Available**: +- Arithmetic: `+`, `-`, `*`, `/` +- Comparison: `=`, `<>`, `<`, `>`, `<=`, `>=` +- Logical: `AND`, `OR`, `NOT` +- Functions: `IF/THEN/ELSE/ENDIF`, `RESULTLOOKUP`, `MEMBERSET` + +### 3. Allocation Step + +Distribute values based on driver data. + +**Configuration**: +``` +Source: + Account: Overhead_Costs + CostCenter: CORPORATE + +Target: + Account: Allocated_Overhead + CostCenter: All leaf members + +Driver: + Account: Headcount + CostCenter: All leaf members + +Method: Proportional +``` + +**Allocation Methods**: +- **Proportional**: Based on driver ratios +- **Equal**: Divide equally +- **Fixed Percentage**: User-defined % + +### 4. Currency Conversion Step + +Convert values between currencies. + +**Configuration**: +``` +Source Currency: Local +Target Currency: Reporting (USD) +Rate Type: Average +Rate Date: Current Period +``` + +### 5. Embedded Data Action Step + +Call another data action from within this action. + +**Use Cases**: +- Reuse common calculation logic +- Break complex processes into modules +- Maintain single source of truth + +**Important**: Parameters from embedded action must be mapped or set. + +--- + +## Multi Actions Overview + +Multi actions orchestrate multiple planning operations across models and versions. Unlike data actions, multi actions can span multiple models and don't run in a single transaction. + +### Key Characteristics + +- **Multiple Models**: Can operate across models +- **Multiple Versions**: Can target different versions +- **Independent Steps**: Each step is separate transaction +- **Partial Success**: Earlier steps persist if later steps fail + +### Creating a Multi Action + +1. Navigate to **Files** → **Create New** → **Multi Action** +2. Add steps in desired sequence +3. Configure parameters +4. Link parameters to steps +5. Save and test + +--- + +## Multi Action Step Types + +### 1. Data Action Step + +Executes a data action with specified parameters. + +**Configuration**: +- Select data action +- Map parameters from multi action to data action parameters +- Or set fixed values + +### 2. Version Management Step + +Publishes a planning version. + +**Configuration**: +- Select model +- Select version to publish +- Use parameter or fixed value + +**Actions**: +- Publish (merge private to public) +- Publish As (create new public version) + +### 3. Predictive Step + +Runs predictive forecasting scenario. + +**Configuration**: +- Select predictive scenario +- Set forecast periods +- Configure target version + +**Predictive Types**: +- Auto (automatic algorithm selection) +- Triple Exponential Smoothing +- Linear Regression + +### 4. Data Import Step + +Import data from SAP sources. + +**Supported Sources**: +- SAP S/4HANA +- SAP BW +- SAP Datasphere +- Other configured connections + +### 5. API Step + +Call external HTTP APIs. + +**Configuration**: +``` +URL: [https://api.example.com/endpoint](https://api.example.com/endpoint) +Method: POST +Headers: + Content-Type: application/json + Authorization: Bearer {{token}} +Body: + { + "planningCycle": "{{PlanningCycle}}", + "region": "{{Region}}" + } +``` + +**Use Cases**: +- Trigger external workflows +- Send notifications +- Integrate with third-party systems + +### 6. Data Locking Step + +Lock or unlock data slices. + +**Configuration**: +- Select model +- Define data slice (dimensions/members) +- Set target lock state (Locked, Restricted, Open) + +### 7. PaPM Step (2025+) + +Run Profitability and Performance Management calculations. + +**Configuration**: +- Select PaPM function +- Set input parameters +- Configure output mapping + +--- + +## Parameters Configuration + +### Parameter Types + +| Type | Description | Available In | +|------|-------------|--------------| +| Member | Select dimension member | Data Actions, Multi Actions | +| Number | Enter numeric value | Data Actions, Multi Actions | +| String | Enter text value | Multi Actions (2025+) | +| Datetime | Select date/time | Multi Actions (2025+) | + +### Creating Parameters + +**In Data Action**: +1. Open data action +2. Click **Parameters** in toolbar +3. Add parameter with name and type +4. Reference in steps: `{{ParameterName}}` + +**In Multi Action**: +1. Open multi action +2. Click **(Available Parameters)** +3. Select **Create Parameter** +4. Configure and use in steps + +### Cross-Model Parameters + +When using **public dimensions** shared across models: + +1. Create parameter based on public dimension +2. Check **Cross-Model Parameter** option +3. Parameter value applies to all steps using that dimension + +### Parameter Assignment + +**Member Selection**: User picks from dimension +**Default Value**: Pre-set value +**Story Filter**: Uses current story filter +**Input Control**: Links to story input control + +--- + +## Execution Methods + +### From Story/Application + +**Button Trigger**: +```javascript +// In button onClick event +DataAction_Budget.setParameterValue("Year", "2025"); +DataAction_Budget.execute(); +``` + +**On Data Change**: +```javascript +// In onResultChanged event (use carefully) +if (shouldRecalculate) { + DataAction_Calc.execute(); +} +``` + +### From Calendar + +1. Create **Data Action Task** or **Multi Action Task** +2. Attach to planning process +3. Schedule execution date/time +4. Assign responsible user + +### Scheduled (Background) + +1. Open Calendar +2. Create **Scheduled Task** +3. Select data action or multi action +4. Set recurring schedule (daily, weekly, etc.) + +### API Execution + +```javascript +// Execute synchronously +DataAction_1.execute(); + +// Execute in background (non-blocking) +DataAction_1.executeInBackground(); + +// With status callback (in onExecutionStatusUpdate event) +// Handle: Running, Success, Failed, Cancelled +``` + +--- + +## Performance Best Practices + +### 1. Scope Reduction + +``` +// BAD: Process all data +Source: All Years, All CostCenters, All Accounts + +// GOOD: Use parameters to limit scope +Source: Year = {{SelectedYear}}, CostCenter = {{SelectedCC}} +``` + +### 2. Input Control Linking + +Link data action parameters to story input controls: +- Reduces processed data volume +- Improves execution time +- Better user experience + +### 3. Embedding for Same Model + +When multiple data actions target same model/version: +1. Create container data action +2. Embed related actions as steps +3. Runs as single transaction (faster) + +### 4. Multi Action Optimization + +``` +// Instead of: +Multi Action with 5 separate data action steps on same model + +// Use: +Embedded data action containing 5 steps ++ Multi Action with 1 data action step +``` + +### 5. Avoid Unnecessary Steps + +- Remove debugging steps before production +- Consolidate similar calculations +- Use RESULTLOOKUP instead of multiple copy steps + +### 6. Tracing for Debugging + +Enable **Tracing Table** during development: +1. Run data action +2. Check "Show Only Leaves" option +3. Review cell-by-cell results +4. Disable before production deployment + +--- + +## Troubleshooting + +### Common Errors + +**"No data to process"** +- Source scope returns no data +- Check dimension filters +- Verify source version has data + +**"Target version is locked"** +- Data locking preventing writes +- Check lock state and owners +- Use appropriate version + +**"Parameter value required"** +- Missing parameter value +- Set default or make optional +- Check parameter mapping + +**"Formula syntax error"** +- Check operator syntax +- Verify member IDs +- Use dimension/member browser + +**"Timeout exceeded"** +- Scope too large +- Add filters to reduce data +- Consider background execution + +### Debugging Steps + +1. **Check Source Data** + - Verify data exists in source scope + - Test with smaller scope first + +2. **Review Tracing** + - Enable tracing table + - Check intermediate results + - Identify failing step + +3. **Test Parameters** + - Run with hardcoded values first + - Then add parameters one by one + +4. **Check Permissions** + - User must have write access to target + - Version must be writable + +5. **Review Logs** + - Check browser console for errors + - Review data action execution log + +--- + +## Example: Complete Planning Workflow + +### Scenario +Annual budget planning: Copy actuals, apply growth rate, allocate overhead + +### Data Action: Budget_Calculation + +**Step 1: Copy Actuals to Budget** +``` +Type: Copy +Source: Version=Actual, Year=2024 +Target: Version=Budget, Year=2025 +Mapping: All dimensions same +``` + +**Step 2: Apply Growth Rate** +``` +Type: Advanced Formula +Formula: +[Account].[Revenue] = [Account].[Revenue] * (1 + {{GrowthRate}}) +[Account].[COGS] = [Account].[COGS] * (1 + {{GrowthRate}} * 0.8) +``` + +**Step 3: Calculate Gross Profit** +``` +Type: Advanced Formula +Formula: +[Account].[Gross_Profit] = [Account].[Revenue] - [Account].[COGS] +``` + +### Multi Action: Annual_Planning_Workflow + +**Step 1: Data Action** +- Action: Budget_Calculation +- Parameters: GrowthRate = {{GrowthRate}} + +**Step 2: Predictive** +- Scenario: Sales_Forecast +- Periods: 12 +- Target: Version=Forecast + +**Step 3: Version Management** +- Action: Publish +- Version: Budget +- Model: Finance_Model + +**Step 4: Data Locking** +- State: Locked +- Slice: Version=Budget, Year=2025 + +--- + +**Documentation Links**: +- Data Actions: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/69a370e6cfd84315973101389baacde0.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/69a370e6cfd84315973101389baacde0.html) +- Multi Actions: [https://learning.sap.com/learning-journeys/leveraging-sap-analytics-cloud-functionality-for-enterprise-planning/configuring-multi-actions](https://learning.sap.com/learning-journeys/leveraging-sap-analytics-cloud-functionality-for-enterprise-planning/configuring-multi-actions) +- Parameters: [https://help.sap.com/doc/00f68c2e08b941f081002fd3691d86a7/2023.20/en-US/4835429d35534add875bae17e93b12e1.html](https://help.sap.com/doc/00f68c2e08b941f081002fd3691d86a7/2023.20/en-US/4835429d35534add875bae17e93b12e1.html) diff --git a/references/data-locking.md b/references/data-locking.md new file mode 100644 index 0000000..1926673 --- /dev/null +++ b/references/data-locking.md @@ -0,0 +1,27 @@ +# Data Locking (SAC 2025.23) + +Purpose: protect planning data by setting lock states on model slices; integrate with multi actions and calendar tasks. + +## Concepts +- **States**: Open (editable), Restricted (owner-only), Locked (no changes). +- **Scopes**: defined by driving dimensions (e.g., Version, Time, Org). +- **Owners**: users/teams with effective ownership for Restricted. + +## Configuration (modeler) +- Enable data locking on planning model. +- Define default lock state and driving dimensions. +- Assign owners where needed for Restricted. + +## Applying locks +- In modeler UI or Data Locking task. +- In multi action via Data Locking Step (set state per filters). +- Respect planning area: locks intersect with planning area scope during edit runs. + +## Scheduling +- Use calendar Data Locking tasks to open/close periods; combine with data/multi action tasks. + +## Troubleshooting +- Publish failures often due to locks; job monitor shows blocked records. +- Adjust locks or use “publish and ignore warnings” to drop blocked records. + +Source: 016-data-locking-configuring.md (SAC Help 2025.23). diff --git a/references/input-tasks.md b/references/input-tasks.md new file mode 100644 index 0000000..c2b42e9 --- /dev/null +++ b/references/input-tasks.md @@ -0,0 +1,21 @@ +# Input & Review Tasks (SAC 2025.23) + +Purpose: structure human data collection and approvals for planning. + +## Task types +- **Input Task**: assign data entry on a planning model slice. +- **Review Task**: request approval/feedback after input. +- **Composite Task**: bundle input + review + dependencies. + +## Setup essentials (010, 011) +- Select model and version; define scope (dimensions/time) for assignees. +- Set due dates, reminders, and recurrence if needed. +- Attach instructions/files; add reviewers for approval path. +- Track status in calendar; tasks can gate subsequent automated steps. + +## Good practices +- Align task scope with planning area to avoid partial edits during actions. +- Use clear naming including model/version and period. +- Keep dependencies minimal but explicit for sequencing. + +Sources: 010-input-tasks.md, 011-plan-data-input-tasks-assigned.md (SAC Help 2025.23). diff --git a/references/javascript-patterns.md b/references/javascript-patterns.md new file mode 100644 index 0000000..5f18956 --- /dev/null +++ b/references/javascript-patterns.md @@ -0,0 +1,1081 @@ +# SAP Analytics Cloud - JavaScript Patterns for Planning + +**Sources**: +- [https://www.denisreis.com/sap-analytics-cloud-javascript-api-code-snippets/](https://www.denisreis.com/sap-analytics-cloud-javascript-api-code-snippets/) +- [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html) +**Last Updated**: 2025-11-22 + +--- + +## Table of Contents + +1. [Application Patterns](#application-patterns) +2. [Member Filtering Patterns](#member-filtering-patterns) +3. [Data Entry Patterns](#data-entry-patterns) +4. [Version Management Patterns](#version-management-patterns) +5. [Data Action Patterns](#data-action-patterns) +6. [Navigation Patterns](#navigation-patterns) +7. [Error Handling Patterns](#error-handling-patterns) +8. [Performance Patterns](#performance-patterns) + +--- + +## Application Patterns + +### Application Initialization + +```javascript +// onInitialization event +Application.showBusyIndicator(); + +try { + // Set default filters + var currentYear = new Date().getFullYear().toString(); + Table_1.getDataSource().setDimensionFilter("Year", currentYear); + + // Get user info for personalization + var userInfo = Application.getUserInfo(); + Text_Welcome.setText("Welcome, " + userInfo.displayName); + + // Load user preferences from app state + loadUserPreferences(); + +} catch (e) { + console.log("Initialization error: " + e.message); +} finally { + Application.hideBusyIndicator(); +} +``` + +### Show/Hide Busy Indicator + +```javascript +// Wrap long operations +function performLongOperation() { + Application.showBusyIndicator(); + + // Do work... + processData(); + + Application.hideBusyIndicator(); +} + +// With error handling +function safeOperation() { + Application.showBusyIndicator(); + try { + riskyOperation(); + } catch (e) { + Application.showMessage("Error: " + e.message); + } finally { + Application.hideBusyIndicator(); + } +} +``` + +### Message Handling + +```javascript +// Simple toast message +Application.showMessage("Operation completed successfully"); + +// For critical messages, use custom dialog +function showErrorDialog(title, message) { + // Assuming Dialog_Error is a popup widget + Text_ErrorTitle.setText(title); + Text_ErrorMessage.setText(message); + Popup_Error.open(); +} +``` + +--- + +## Member Filtering Patterns + +### Find Active Member by Attribute + +```javascript +// Find version marked as "Active" in master data +function findActivePlanVersion() { + var allVersions = PlanningModel_1.getMembers("Version"); + + for (var i = 0; i < allVersions.length; i++) { + if (allVersions[i].properties.Active === "X") { + return allVersions[i].id; + } + } + return null; // No active version found +} + +// Usage +var activeVersion = findActivePlanVersion(); +if (activeVersion) { + Table_1.getDataSource().setDimensionFilter("Version", + "[Version].[parentId].&[public." + activeVersion + "]"); +} +``` + +### Get Booked Values Only + +```javascript +// Performance optimization: only get members with data +var bookedAccounts = Table_1.getDataSource().getMembers("Account", { + accessMode: MemberAccessMode.BookedValues +}); + +console.log("Found " + bookedAccounts.length + " accounts with data"); +``` + +### Filter by Property Value + +```javascript +// Get all cost centers for a specific region +function getCostCentersByRegion(region) { + var allCCs = PlanningModel_1.getMembers("CostCenter", { + limit: 10000 + }); + + var regionCCs = []; + for (var i = 0; i < allCCs.length; i++) { + if (allCCs[i].properties.Region === region) { + regionCCs.push(allCCs[i].id); + } + } + return regionCCs; +} + +// Apply as filter +var emeaCCs = getCostCentersByRegion("EMEA"); +Table_1.getDataSource().setDimensionFilter("CostCenter", emeaCCs); +``` + +### Dynamic Planning Cycle Filter + +```javascript +// Set filter based on active planning cycle from master data +function setActivePlanningCycleFilter() { + Application.showBusyIndicator(); + Table_1.setVisible(false); + + var cycles = PlanningModel_1.getMembers("PlanningCycle"); + var activeCycle = ""; + + for (var i = 0; i < cycles.length; i++) { + if (cycles[i].properties.Flag === "PC+0") { + activeCycle = cycles[i].id; + break; + } + } + + if (activeCycle) { + // MDX filter syntax + Table_1.getDataSource().setDimensionFilter("Date", + "[Date].[YQM].&[" + activeCycle + "]"); + } + + Table_1.setVisible(true); + Application.hideBusyIndicator(); +} +``` + +### Populate Dropdown from Members + +```javascript +// Fill dropdown with dimension members +function populateDropdown(dropdown, dimensionId, filterProperty, filterValue) { + dropdown.removeAllItems(); + + var members = PlanningModel_1.getMembers(dimensionId, {limit: 1000}); + + for (var i = 0; i < members.length; i++) { + var member = members[i]; + + // Optional: filter by property + if (filterProperty && member.properties[filterProperty] !== filterValue) { + continue; + } + + dropdown.addItem(member.id, member.description || member.id); + } +} + +// Usage +populateDropdown(Dropdown_CostCenter, "CostCenter", "Status", "Active"); +``` + +--- + +## Data Entry Patterns + +### Check Planning Enabled + +```javascript +// Verify planning is enabled before operations +function verifyPlanningEnabled() { + var planning = Table_1.getPlanning(); + + if (!planning || !planning.isEnabled()) { + Application.showMessage("Planning is not enabled for this table"); + return false; + } + return true; +} +``` + +### Set Cell Value + +```javascript +// Set value for selected cell +function setSelectedCellValue(value) { + if (!verifyPlanningEnabled()) return; + + var selections = Table_1.getSelections(); + if (selections.length === 0) { + Application.showMessage("Please select a cell first"); + return; + } + + var selection = selections[0]; + + // Check data locking + var dataLocking = Table_1.getPlanning().getDataLocking(); + var lockState = dataLocking.getState(selection); + + if (lockState === DataLockingState.Locked) { + Application.showMessage("This cell is locked and cannot be edited"); + return; + } + + // Set the value + Table_1.getPlanning().setUserInput(selection, value.toString()); +} +``` + +### Apply Factor to Value + +```javascript +// Multiply existing value by factor +function applyFactor(factor) { + if (!verifyPlanningEnabled()) return; + + var selection = Table_1.getSelections()[0]; + if (!selection) return; + + // Prefix with "*" to multiply + Table_1.getPlanning().setUserInput(selection, "*" + factor.toString()); +} + +// Usage: increase by 10% +applyFactor(1.1); + +// Usage: decrease by 50% +applyFactor(0.5); +``` + +### Enable/Disable Planning Based on Business Rules + +```javascript +// Disable planning in Q4 (budget freeze) +function checkPlanningAvailability() { + var currentMonth = new Date().getMonth() + 1; + + if (currentMonth >= 10 && currentMonth <= 12) { + Table_Budget.getPlanning().setEnabled(false); + Button_Save.setEnabled(false); + Application.showMessage("Budget changes locked in Q4"); + } else { + Table_Budget.getPlanning().setEnabled(true); + Button_Save.setEnabled(true); + } +} +``` + +### Submit Data Changes + +```javascript +// Submit pending changes +function submitPlanningData() { + if (!verifyPlanningEnabled()) return; + + Application.showBusyIndicator(); + + try { + Table_1.getPlanning().submitData(); + Application.showMessage("Data submitted successfully"); + } catch (e) { + Application.showMessage("Submit failed: " + e.message); + } finally { + Application.hideBusyIndicator(); + } +} +``` + +### Bulk Data Entry + +```javascript +// Set values for multiple cells +function setBulkValues(valueMap) { + // valueMap: [{selection: {...}, value: "100"}, ...] + + Application.showBusyIndicator(); + + var planning = Table_1.getPlanning(); + var successCount = 0; + var errorCount = 0; + + for (var i = 0; i < valueMap.length; i++) { + try { + planning.setUserInput(valueMap[i].selection, valueMap[i].value); + successCount++; + } catch (e) { + errorCount++; + console.log("Error setting value: " + e.message); + } + } + + // Submit all changes at once + planning.submitData(); + + Application.showMessage("Set " + successCount + " values, " + errorCount + " errors"); + Application.hideBusyIndicator(); +} +``` + +--- + +## Version Management Patterns + +### Get All Versions + +```javascript +// List all available versions +function listVersions() { + var planning = Table_1.getPlanning(); + + console.log("=== Public Versions ==="); + var publicVersions = planning.getPublicVersions(); + for (var i = 0; i < publicVersions.length; i++) { + console.log(publicVersions[i].id + ": " + publicVersions[i].description); + } + + console.log("=== Private Versions ==="); + var privateVersions = planning.getPrivateVersions(); + for (var j = 0; j < privateVersions.length; j++) { + console.log(privateVersions[j].id + ": " + privateVersions[j].description); + } +} +``` + +### Publish Version + +```javascript +// Publish private version or edit mode +function publishVersion() { + var planning = Table_1.getPlanning(); + var privateVersion = planning.getPrivateVersion(); + + if (!privateVersion) { + Application.showMessage("No private version to publish"); + return; + } + + if (!privateVersion.isDirty()) { + Application.showMessage("No changes to publish"); + return; + } + + Application.showBusyIndicator(); + + try { + privateVersion.publish(); + Application.showMessage("Version published successfully"); + } catch (e) { + Application.showMessage("Publish failed: " + e.message); + } finally { + Application.hideBusyIndicator(); + } +} +``` + +### Publish As New Version + +```javascript +// Create new public version from private +function publishAsNewVersion(newVersionId, newDescription) { + var planning = Table_1.getPlanning(); + var privateVersion = planning.getPrivateVersion(); + + if (!privateVersion) { + Application.showMessage("No private version available"); + return; + } + + Application.showBusyIndicator(); + + try { + privateVersion.publishAs(newVersionId, newDescription); + Application.showMessage("New version '" + newVersionId + "' created"); + } catch (e) { + Application.showMessage("Publish As failed: " + e.message); + } finally { + Application.hideBusyIndicator(); + } +} + +// Usage +publishAsNewVersion("Budget_2025_v2", "Revised Budget 2025"); +``` + +### Revert Changes + +```javascript +// Discard all changes +function revertChanges() { + var planning = Table_1.getPlanning(); + var privateVersion = planning.getPrivateVersion(); + + if (!privateVersion) { + Application.showMessage("No private version to revert"); + return; + } + + // Confirm before reverting + // (In real app, use custom confirmation dialog) + privateVersion.revert(); + Application.showMessage("Changes reverted"); +} +``` + +### Check Dirty Status Before Publishing + +```javascript +// Use isDirty() to avoid unnecessary publish attempts +function publishIfDirty() { + var version = Table_1.getPlanning().getPublicVersion("Forecast"); + + if (!version.isDirty()) { + Application.showMessage("No changes to publish"); + return; + } + + Application.showBusyIndicator(); + var success = version.publish(); + Application.hideBusyIndicator(); + + if (success) { + Application.showMessage("Published successfully"); + } else { + Application.showMessage("Publish failed"); + } +} +``` + +### Copy Version with Options + +```javascript +// Create new version from existing +function copyVersionAsNewBudget(sourceId, targetId) { + var planning = Table_1.getPlanning(); + var source = planning.getPublicVersion(sourceId); + + if (!source) { + Application.showMessage("Source version not found"); + return; + } + + Application.showBusyIndicator(); + + var success = source.copy( + targetId, + PlanningCopyOptions.AllData, + PlanningCategory.Budget + ); + + Application.hideBusyIndicator(); + + if (success) { + Application.showMessage("Budget " + targetId + " created"); + Application.refreshData(); + } +} +``` + +### Version Selection UI + +```javascript +// Populate version dropdown and handle selection +function setupVersionSelector() { + var planning = Table_1.getPlanning(); + var versions = planning.getPublicVersions(); + + Dropdown_Version.removeAllItems(); + for (var i = 0; i < versions.length; i++) { + Dropdown_Version.addItem(versions[i].id, versions[i].description); + } +} + +// onSelect event for Dropdown_Version +function onVersionSelected() { + var selectedVersion = Dropdown_Version.getSelectedKey(); + + Table_1.getDataSource().setDimensionFilter("Version", + "[Version].[parentId].&[public." + selectedVersion + "]"); +} +``` + +--- + +## Data Action Patterns + +### Execute with Parameters + +```javascript +// Execute data action with parameters +function executeDataAction() { + // Set parameters + DataAction_Copy.setParameterValue("SourceVersion", "Actual"); + DataAction_Copy.setParameterValue("TargetVersion", Dropdown_TargetVersion.getSelectedKey()); + DataAction_Copy.setParameterValue("Year", InputField_Year.getValue()); + + Application.showBusyIndicator(); + DataAction_Copy.execute(); +} + +// In onExecutionComplete event +function onDataActionComplete() { + Application.hideBusyIndicator(); + Application.showMessage("Data action completed"); + Application.refreshData(); +} +``` + +### Execute in Background + +```javascript +// Non-blocking execution for long-running actions +function executeInBackground() { + DataAction_LargeCalculation.setParameterValue("Scope", "ALL"); + DataAction_LargeCalculation.executeInBackground(); + + Application.showMessage("Data action started in background"); +} + +// Monitor status in onExecutionStatusUpdate event +function onStatusUpdate(status) { + // status can be: Running, Success, Failed, Cancelled + console.log("Status: " + status); + + if (status === "Success" || status === "Failed") { + Application.refreshData(); + } +} +``` + +### Conditional Execution + +```javascript +// Execute different actions based on conditions +function executeConditionalAction() { + var selectedAction = Dropdown_ActionType.getSelectedKey(); + var targetVersion = Dropdown_Version.getSelectedKey(); + + switch (selectedAction) { + case "COPY": + DataAction_Copy.setParameterValue("TargetVersion", targetVersion); + DataAction_Copy.execute(); + break; + case "ALLOCATE": + DataAction_Allocate.setParameterValue("TargetVersion", targetVersion); + DataAction_Allocate.execute(); + break; + case "CLEAR": + DataAction_Clear.setParameterValue("TargetVersion", targetVersion); + DataAction_Clear.execute(); + break; + default: + Application.showMessage("Unknown action type"); + } +} +``` + +--- + +## Navigation Patterns + +### Open Another Story + +```javascript +// Navigate to detail story with context +function openDetailStory(entityId) { + // Pass parameters via URL + var storyId = "story_id_here"; + var params = "?p_entity=" + entityId; + + NavigationUtils.openStory(storyId + params); +} +``` + +### Open External URL + +```javascript +// Open SAP documentation +function openDocumentation() { + NavigationUtils.openUrl( + "[https://help.sap.com/docs/SAP_ANALYTICS_CLOUD"](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD") + ); +} +``` + +### Refresh Data + +```javascript +// Refresh all data sources after external changes +function refreshAllData() { + Application.showBusyIndicator(); + Application.refreshData(); + Application.hideBusyIndicator(); + Application.showMessage("Data refreshed"); +} +``` + +--- + +## Error Handling Patterns + +### Try-Catch Pattern + +```javascript +// Wrap risky operations +function safeOperation() { + try { + riskyFunction(); + Application.showMessage("Success"); + } catch (error) { + console.log("Error: " + error.message); + Application.showMessage("Operation failed: " + error.message); + } +} +``` + +### Validation Pattern + +```javascript +// Validate before operation +function validateAndExecute() { + // Check required selections + var selection = Table_1.getSelections(); + if (selection.length === 0) { + Application.showMessage("Please select data first"); + return false; + } + + // Check required inputs + var year = InputField_Year.getValue(); + if (!year || year.length !== 4) { + Application.showMessage("Please enter valid year (YYYY)"); + return false; + } + + // Check planning enabled + if (!Table_1.getPlanning().isEnabled()) { + Application.showMessage("Planning not enabled"); + return false; + } + + // All validations passed + executeAction(); + return true; +} +``` + +### Null Check Pattern + +```javascript +// Handle potentially null objects +function safeGetVersion() { + var planning = Table_1.getPlanning(); + if (!planning) { + console.log("Planning object not available"); + return null; + } + + var privateVersion = planning.getPrivateVersion(); + if (!privateVersion) { + console.log("No private version exists"); + return null; + } + + return privateVersion; +} +``` + +--- + +## Performance Patterns + +### Limit Member Retrieval + +```javascript +// Always set limit for getMembers +var members = PlanningModel_1.getMembers("CostCenter", { + limit: 5000 // Don't load unlimited members +}); + +// Use offset for pagination +var page2 = PlanningModel_1.getMembers("CostCenter", { + limit: 1000, + offset: 1000 +}); +``` + +### Cache Member Lists + +```javascript +// Store frequently used member lists +var memberCache = {}; + +function getCachedMembers(dimensionId) { + if (!memberCache[dimensionId]) { + memberCache[dimensionId] = PlanningModel_1.getMembers(dimensionId, { + limit: 10000 + }); + } + return memberCache[dimensionId]; +} + +// Clear cache when model changes +function clearMemberCache() { + memberCache = {}; +} +``` + +### Batch UI Updates + +```javascript +// Hide table during batch updates +function batchUpdate() { + Table_1.setVisible(false); + Application.showBusyIndicator(); + + // Perform multiple operations + updateFilters(); + updateSorting(); + updateFormatting(); + + Table_1.setVisible(true); + Application.hideBusyIndicator(); +} +``` + +### Debounce Pattern + +```javascript +// Avoid rapid repeated calls (pseudo-code concept) +var debounceTimer = null; + +function debouncedSearch(searchTerm) { + // Clear previous timer + if (debounceTimer) { + // In SAC, you'd use a different approach + // as clearTimeout isn't directly available + } + + // Delay execution + // Note: SAC doesn't have setTimeout, use onTimeout event instead + performSearch(searchTerm); +} +``` + +--- + +## Data Locking Patterns + +### Check Lock State Before Editing + +```javascript +// Verify cell can be edited before allowing user input +function canEditCell(selection) { + var dataLocking = Table_1.getPlanning().getDataLocking(); + + if (!dataLocking) { + return true; // Data locking not enabled, allow edit + } + + var lockState = dataLocking.getState(selection); + + switch (lockState) { + case DataLockingState.Open: + return true; + case DataLockingState.Restricted: + Application.showMessage("Only data owner can edit this cell"); + return false; + case DataLockingState.Locked: + Application.showMessage("This data is locked and cannot be edited"); + return false; + case DataLockingState.Mixed: + Application.showMessage("Selection contains mixed lock states"); + return false; + default: + console.log("Unknown lock state"); + return false; + } +} +``` + +### Set Lock State on Public Version + +```javascript +// Lock data after approval +function lockApprovedData() { + var selection = Table_1.getSelections()[0]; + var dataLocking = Table_1.getPlanning().getDataLocking(); + + if (!dataLocking) { + Application.showMessage("Data locking not enabled on this model"); + return; + } + + // Note: Can only set lock state on public versions + var success = dataLocking.setState(selection, DataLockingState.Locked); + + if (success) { + Application.showMessage("Data locked successfully"); + } else { + Application.showMessage("Failed to lock data - ensure you're on a public version"); + } +} +``` + +--- + +## Members on the Fly Patterns + +### Create New Dimension Member + +```javascript +// Dynamically add new cost center +function createCostCenter(id, description, region) { + Application.showBusyIndicator(); + + try { + PlanningModel_1.createMembers("CostCenter", { + id: id, + description: description, + properties: { + "CUSTOM_Region": region, + "CUSTOM_Status": "Active" + } + }); + + // IMPORTANT: Refresh to see new member + Application.refreshData(); + + Application.showMessage("Cost center created: " + id); + } catch (e) { + Application.showMessage("Error: " + e.message); + } finally { + Application.hideBusyIndicator(); + } +} +``` + +### Update Member with Data Locking Owner + +```javascript +// Assign data locking ownership +function assignDataOwner(dimensionId, memberId, userId) { + PlanningModel_1.updateMembers(dimensionId, { + id: memberId, + dataLockingOwners: [{ + id: userId, + type: UserType.User + }] + }); + + Application.refreshData(); + Application.showMessage("Data owner assigned"); +} +``` + +### Get Members with Pagination + +```javascript +// Load members in pages for large dimensions +function loadMembersInPages(dimensionId, pageSize) { + var allMembers = []; + var offset = 0; + var hasMore = true; + + while (hasMore) { + var page = PlanningModel_1.getMembers(dimensionId, { + offset: offset.toString(), + limit: pageSize.toString() + }); + + if (page.length > 0) { + allMembers = allMembers.concat(page); + offset += pageSize; + } else { + hasMore = false; + } + } + + return allMembers; +} +``` + +--- + +## Utility Patterns + +### Type Conversion + +```javascript +// Convert string to integer +var planningCycle = "2025"; +var cycleNumber = ConvertUtils.stringToInteger(planningCycle); +cycleNumber++; +var nextCycle = ConvertUtils.numberToString(cycleNumber); // "2026" +``` + +### Date Formatting + +```javascript +// Format current date +var today = new Date(); +var formatted = DateFormat.format(today, "yyyy-MM-dd"); +console.log("Today: " + formatted); +``` + +### Number Formatting + +```javascript +// Format currency value +var amount = 1234567.89; +var formatted = NumberFormat.format(amount, "#,##0.00"); +console.log("Amount: " + formatted); // "1,234,567.89" +``` + +### Array Operations + +```javascript +// Check if array contains value +var selectedItems = ["A", "B", "C"]; +if (ArrayUtils.contains(selectedItems, "B")) { + console.log("B is selected"); +} + +// Find index +var index = ArrayUtils.indexOf(selectedItems, "C"); // 2 +``` + +--- + +## Complete Example: Budget Entry Application + +```javascript +// === onInitialization Event === +Application.showBusyIndicator(); + +// Set default version +var versions = Table_Budget.getPlanning().getPublicVersions(); +for (var i = 0; i < versions.length; i++) { + Dropdown_Version.addItem(versions[i].id, versions[i].description); +} + +// Set current year +var currentYear = new Date().getFullYear().toString(); +InputField_Year.setValue(currentYear); + +// Load user's cost centers +var userInfo = Application.getUserInfo(); +loadUserCostCenters(userInfo.userId); + +Application.hideBusyIndicator(); + + +// === Version Selection === +function onVersionSelected() { + var version = Dropdown_Version.getSelectedKey(); + Table_Budget.getDataSource().setDimensionFilter("Version", + "[Version].[parentId].&[public." + version + "]"); +} + + +// === Save Button === +function onSaveClick() { + if (!Table_Budget.getPlanning().isEnabled()) { + Application.showMessage("Planning not enabled"); + return; + } + + Application.showBusyIndicator(); + + try { + Table_Budget.getPlanning().submitData(); + Application.showMessage("Budget saved successfully"); + } catch (e) { + Application.showMessage("Save failed: " + e.message); + } finally { + Application.hideBusyIndicator(); + } +} + + +// === Publish Button === +function onPublishClick() { + var privateVer = Table_Budget.getPlanning().getPrivateVersion(); + + if (!privateVer || !privateVer.isDirty()) { + Application.showMessage("No changes to publish"); + return; + } + + Application.showBusyIndicator(); + + try { + privateVer.publish(); + Application.showMessage("Budget published successfully"); + } catch (e) { + Application.showMessage("Publish failed: " + e.message); + } finally { + Application.hideBusyIndicator(); + } +} + + +// === Apply Growth Rate === +function onApplyGrowthClick() { + var growthRate = ConvertUtils.stringToNumber(InputField_GrowthRate.getValue()); + + if (isNaN(growthRate)) { + Application.showMessage("Invalid growth rate"); + return; + } + + var factor = 1 + (growthRate / 100); + + var selections = Table_Budget.getSelections(); + if (selections.length === 0) { + Application.showMessage("Select cells to apply growth"); + return; + } + + Application.showBusyIndicator(); + + for (var i = 0; i < selections.length; i++) { + Table_Budget.getPlanning().setUserInput(selections[i], "*" + factor); + } + + Table_Budget.getPlanning().submitData(); + Application.showMessage("Growth rate applied"); + Application.hideBusyIndicator(); +} +``` + +--- + +**Documentation Links**: +- API Reference: [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html) +- Code Snippets Blog: [https://www.denisreis.com/sap-analytics-cloud-javascript-api-code-snippets/](https://www.denisreis.com/sap-analytics-cloud-javascript-api-code-snippets/) +- SAP Community Scripting: [https://community.sap.com/t5/technology-blog-posts-by-sap/start-your-scripting-journey-the-easy-way-with-sap-analytics-cloud-part/ba-p/13582659](https://community.sap.com/t5/technology-blog-posts-by-sap/start-your-scripting-journey-the-easy-way-with-sap-analytics-cloud-part/ba-p/13582659) diff --git a/references/job-monitoring.md b/references/job-monitoring.md new file mode 100644 index 0000000..6374ff6 --- /dev/null +++ b/references/job-monitoring.md @@ -0,0 +1,21 @@ +# Job Monitoring & Troubleshooting (SAC 2025.23) + +Use the Job Monitor to track data actions and multi actions started from stories, analytic apps, or calendar tasks. + +## What to check +- **Status**: success / failed / success with warnings; background runs show completion here. +- **Step lineage**: which data action or multi action ran, with timestamps. +- **Error messages**: parameter validation, data locks, booking on non-leaf members, mapping gaps in cross-model copy. + +## Quick triage +- Parameter errors → fix prompt values or mapping; rerun. +- Data locked / publish failed → unlock or adjust planning area, rerun publish step. +- Non-leaf bookings → adjust source hierarchy or mapping to leaf members. +- Timeouts on steps → break into smaller slices (time/account filters) or run in background. + +## Good practices +- Keep runs small: filter versions/time, append vs overwrite intentionally. +- Name runs clearly; include version/model in starter labels. +- After background completion, refresh story/app before further edits. + +Related: `references/data-actions.md` for design patterns, `references/multi-actions.md` for step-level behaviors. diff --git a/references/modeling-basics.md b/references/modeling-basics.md new file mode 100644 index 0000000..432fc62 --- /dev/null +++ b/references/modeling-basics.md @@ -0,0 +1,30 @@ +# Planning Model Basics (SAC 2025.23) + +Sources: 003-about-planning.md, 004-planning-model-data.md, 005-enter-planning-data-tables.md, 006-planning-panel.md, 009-create-dimension-members.md. + +## Required dimensions +- **Version** (mandatory) and **Date** (mandatory). +- **Account** recommended for measures; include hierarchy and sign rules. +- Add business dimensions (Org, Product, etc.) with hierarchies for planning grain. + +## Model data setup +- Fact data stored at leaf members; avoid booking to non-leaf (causes action errors). +- Use data foundation to define measures, currency/unit settings, and validation rules. +- Enable data access control where needed for write permissions. + +## Planning panel essentials (stories) +- Enable planning on tables; choose version (public/private/edit mode). +- Spreading/distribution from the Planning panel for quick allocations. +- Data locking status visible; submit data after entry. + +## Entering data +- Editable cells depend on model rights + locks + version mode. +- Use mass data entry/spreading; undo available before submit. + +## Master data maintenance +- Create/update/delete members via PlanningModel API or modeler; ensure IDs unique and hierarchies updated. + +## Good practices +- Define clear leaf level for booking; reserve aggregated nodes for read only. +- Keep dimensions consistent across models to simplify cross-model copy and parameters. +- Test write-back with small scope before broad rollout. diff --git a/references/multi-actions.md b/references/multi-actions.md new file mode 100644 index 0000000..83f58f4 --- /dev/null +++ b/references/multi-actions.md @@ -0,0 +1,31 @@ +# Multi Actions — Quick Reference (SAC 2025.23) + +Purpose: orchestrate cross-model planning workflows (data actions, publishing, predictive, imports, API calls, data locking) with parameterized, restartable steps. + +## When to choose multi actions +- Need to sequence multiple data actions or combine with publishing/predictive/API steps. +- Operations span several models/versions or can tolerate partial success (steps are independent transactions). +- Want calendar scheduling or a single starter in story/app for end users. + +## Core step types (designer toolbar) +- **Data Action Step**: run a data action; map multi-action params; optional auto-publish with fail/ignore warnings; can limit to recommended planning area when target public version not in edit mode. +- **Version Management Step**: publish a version; options: fail on warnings vs ignore warnings. +- **Predictive Step**: time-series scenario; actions: Train & Forecast, Train Only, Apply Only; choose input/output versions; optional past-period values and prediction interval versions. +- **Data Import Step**: run import job (model or master data). Unsupported: job groups, export jobs, Concur/ERP/Fieldglass/Dataset, local-file imported models. +- **API Step**: HTTP POST with headers/body; supports CSRF token fetch; sync/async result mapping; parameters can be inserted into payload. +- **PaPM Integration Step**: trigger PaPM process/activity with parameters and follow-up options (delete process, mark complete). +- **Data Locking Step**: set lock state (Open/Locked/Restricted) via driving-dimension filters. + +## Execution & background behavior +- Each step commits independently; earlier steps persist if later steps fail (status may be *successful with warning*). +- Background runs allowed; users must wait for completion before editing same version; refresh to view results. + +## Parameter patterns +- Reuse member/number/measure parameters across steps; cross-model parameters reuse shared public dimensions. +- For embedded data actions, the container must still map/set each embedded parameter; you can apply stricter Level/cardinality than the embedded object. + +## Troubleshooting signals +- Step failure messages identify step type/name. +- Data action issues surface in job monitor; version publish can fail on data locks; API steps expose response mapping for error fields. + +Related: `references/data-actions.md` (data-action design), `references/job-monitoring.md` (diagnostics), `references/version-edit-modes.md` (publish behaviors). diff --git a/references/planning-workflows.md b/references/planning-workflows.md new file mode 100644 index 0000000..24f3b49 --- /dev/null +++ b/references/planning-workflows.md @@ -0,0 +1,543 @@ +# SAP Analytics Cloud - Planning Workflows and Calendar Reference + +**Source**: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/af4b7e39edd249d3b59fa7d4ab110a7a.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/af4b7e39edd249d3b59fa7d4ab110a7a.html) +**Last Updated**: 2025-11-22 + +--- + +## Table of Contents + +1. [Calendar Overview](#calendar-overview) +2. [Planning Processes](#planning-processes) +3. [Task Types](#task-types) +4. [Multi-Level Approvals](#multi-level-approvals) +5. [Data Locking Integration](#data-locking-integration) +6. [Task Dependencies](#task-dependencies) +7. [Notifications and Collaboration](#notifications-and-collaboration) +8. [Best Practices](#best-practices) + +--- + +## Calendar Overview + +The SAP Analytics Cloud calendar organizes collaborative planning workflows, structures planning processes, and monitors progress across teams. + +### Accessing Calendar + +1. Main Menu → **Calendar** +2. Or via direct URL: `[https:///calendar`](https:///calendar`) + +### Calendar Views + +| View | Description | +|------|-------------| +| **Calendar View** | Traditional calendar grid (day/week/month) | +| **List View** | Task list with details | +| **Gantt View** | Timeline with dependencies | + +### Key Capabilities + +- Create and manage planning processes +- Assign tasks to users and teams +- Track completion status +- Configure automatic notifications +- Schedule data actions and multi actions +- Integrate data locking with workflows + +--- + +## Planning Processes + +A planning process is a container for related tasks that make up a complete planning cycle. + +### Creating a Planning Process + +1. Open Calendar +2. Click **Create** → **Planning Process** +3. Configure: + - Name and description + - Start and end dates + - Owner and participants +4. Add tasks to the process + +### Process Properties + +| Property | Description | +|----------|-------------| +| Name | Descriptive name | +| Description | Detailed explanation | +| Start Date | Process begins | +| End Date | Process must complete | +| Owner | Responsible user | +| State | Draft, Active, Completed, Cancelled | + +### Process Lifecycle + +``` +Draft → Active → Running → Completed + ↓ + Cancelled +``` + +### Activating a Process + +- **Manual**: Click "Activate" when ready +- **Automatic**: Activates on start date +- **Dependencies**: Activates when predecessor completes + +--- + +## Task Types + +### 1. General Task + +Standard work assignment for data entry or other activities. + +**Configuration**: +``` +Name: Q1 Budget Entry +Description: Enter Q1 budget for your region +Assignees: Regional Managers (multiple) +Due Date: 2025-01-31 +Work File: Budget_Entry_Story +Data Context: Filter to assignee's region +``` + +**Features**: +- Multiple assignees +- Work file attachment (story or application) +- Data context filtering +- Progress tracking per assignee + +**Assignee Actions**: +1. Open task from notification or calendar +2. Click "Open Work File" +3. Enter/modify data +4. Click "Submit" when complete + +### 2. Review Task + +Approval workflow for reviewing submitted work. + +**Configuration**: +``` +Name: Finance Review Q1 Budget +Description: Review and approve regional budgets +Reviewers: Finance Director +Predecessor: Q1 Budget Entry task +Action on Completion: Trigger data locking +``` + +**Reviewer Actions**: +- **Approve**: Accept submission, move to next step +- **Reject**: Return to assignee with comments + +**After Rejection**: +1. Assignee notified +2. Task returns to "In Progress" +3. Assignee revises and resubmits +4. Reviewer notified for re-review + +### 3. Composite Task + +Combined general task and review in single configuration. + +**Use Cases**: +- Simple single-level approval +- When general task and review have same timeline +- Streamlined configuration + +**Configuration**: +``` +Name: Departmental Budget Approval +Assignees: Department Heads +Reviewer: CFO +Driving Dimension: Department +Due Date: 2025-02-15 +``` + +**Driving Dimension**: +- Automatically creates sub-tasks per member +- Each department head sees only their data +- Consolidated review for approver + +### 4. Data Locking Task + +Scheduled data lock state changes. + +**Configuration**: +``` +Name: Lock Q1 Budget +Model: Finance_Planning_Model +Data Slice: + Version: Budget + Year: 2025 + Quarter: Q1 +Target State: Locked +Trigger: On predecessor approval +``` + +**Lock States**: +- **Open**: Anyone with access can edit +- **Restricted**: Only owner can edit +- **Locked**: No edits allowed + +### 5. Data Action Task + +Scheduled execution of data actions. + +**Configuration**: +``` +Name: Run Budget Allocation +Data Action: Overhead_Allocation +Parameters: + Version: Budget + Year: 2025 +Schedule: After Budget Entry approval +``` + +### 6. Multi Action Task + +Scheduled execution of multi actions. + +**Configuration**: +``` +Name: Complete Planning Cycle +Multi Action: Annual_Planning_Workflow +Parameters: + Planning_Cycle: 2025 +Trigger: Process end date +``` + +--- + +## Multi-Level Approvals + +Implement hierarchical approval workflows using sequential review tasks. + +### Configuration Steps + +1. **Create General Task** for data entry +2. **Add Review Task Round 1** (first level approver) + - Set predecessor: General Task +3. **Add Review Task Round 2** (second level approver) + - Set predecessor: Review Task Round 1 +4. **Continue** for additional levels + +### Example: 3-Level Approval + +``` +┌─────────────────────────────────────────┐ +│ General Task: Regional Budget Entry │ +│ Assignees: Regional Planners │ +└─────────────────┬───────────────────────┘ + │ Submit + ▼ +┌─────────────────────────────────────────┐ +│ Review Task Round 1: Regional Review │ +│ Reviewers: Regional Managers │ +└─────────────────┬───────────────────────┘ + │ Approve + ▼ +┌─────────────────────────────────────────┐ +│ Review Task Round 2: Corporate Review │ +│ Reviewers: Finance Director │ +└─────────────────┬───────────────────────┘ + │ Approve + ▼ +┌─────────────────────────────────────────┐ +│ Review Task Round 3: Executive Approval │ +│ Reviewers: CFO │ +└─────────────────┬───────────────────────┘ + │ Approve + ▼ +┌─────────────────────────────────────────┐ +│ Data Locking Task: Lock Approved Budget │ +└─────────────────────────────────────────┘ +``` + +### Rejection Flow + +``` +Round 2 Rejects + ↓ +Round 1 Reviewer notified (optional) + ↓ +Assignee notified, task reopened + ↓ +Assignee revises data + ↓ +Resubmit through all approval levels +``` + +### Configuration Options + +| Option | Description | +|--------|-------------| +| Skip on Rejection | Allow later approver to override earlier rejection | +| Auto-Approve | Automatically approve if no action in X days | +| Escalation | Notify manager if task overdue | + +--- + +## Data Locking Integration + +### Manual Data Locking via Story + +```javascript +// Check lock state +var dataLocking = Table_1.getPlanning().getDataLocking(); +var selection = Table_1.getSelections()[0]; +var state = dataLocking.getState(selection); + +// Set lock state (owner only) +if (state === DataLockingState.Open) { + dataLocking.setState(selection, DataLockingState.Locked); +} +``` + +### Calendar Data Locking Task + +**Trigger Types**: +- **Scheduled**: Specific date/time +- **Event-Based**: After task completion +- **Manual**: User clicks "Submit" + +**Configuration Example**: +``` +Task: Lock Regional Plans +Trigger: After "Regional Review" approval +Model: Finance_Model +Data Slice: + Version: Budget_2025 + Region: [Assignee's Region] +Target State: Locked +Owner Transfer: Yes (to Finance Director) +``` + +### Multi Action Data Locking Step + +Include data locking in automated workflows: + +``` +Multi Action: Close_Planning_Cycle +Steps: + 1. Publish Budget version + 2. Run consolidation data action + 3. Lock Budget version + 4. Send notification via API step +``` + +### Data Locking Best Practices + +1. **Lock progressively** - Lock completed regions while others work +2. **Use restricted state** - Allow corrections before final lock +3. **Assign owners** - Enable self-service lock management +4. **Event-based triggers** - Automate locking after approvals +5. **Document unlock process** - Define who can unlock and when + +--- + +## Task Dependencies + +### Dependency Types + +| Type | Behavior | +|------|----------| +| **Finish-to-Start** | Task B starts when Task A finishes | +| **Start-to-Start** | Task B starts when Task A starts | +| **Finish-to-Finish** | Task B finishes when Task A finishes | + +### Configuring Dependencies + +1. Open task in edit mode +2. Go to **Dependencies** section +3. Select predecessor task +4. Choose dependency type +5. Set lag time (optional) + +### Automatic Activation + +When a task has dependencies: +- Remains inactive until predecessor completes +- Automatically activates on predecessor completion +- Assignees receive notification + +### Example: Dependent Task Chain + +``` +Budget Planning Process (2025-01-01 to 2025-03-31) + +Task 1: Load Actuals (Data Import) + Start: 2025-01-02 + Duration: 1 day + +Task 2: Regional Budget Entry + Start: After Task 1 (Finish-to-Start) + Duration: 14 days + +Task 3: Corporate Review + Start: After Task 2 (Finish-to-Start) + Duration: 5 days + +Task 4: Executive Approval + Start: After Task 3 (Finish-to-Start) + Duration: 3 days + +Task 5: Lock and Archive + Start: After Task 4 (Finish-to-Start) + Duration: 1 day +``` + +--- + +## Notifications and Collaboration + +### Notification Types + +| Event | Recipients | Channel | +|-------|------------|---------| +| Task Assigned | Assignees | Email, In-app | +| Task Due Soon | Assignees | Email | +| Task Overdue | Assignees, Owner | Email | +| Submission Received | Reviewers | Email, In-app | +| Approved | Assignees | Email, In-app | +| Rejected | Assignees | Email, In-app | +| Process Complete | Owner | Email | + +### Configuring Notifications + +1. Open process or task settings +2. Go to **Notifications** tab +3. Enable/disable notification types +4. Set reminder schedules + +### Discussion Feature + +Collaborate directly within calendar: + +1. Open task or process +2. Click **Open Discussion** +3. Start conversation with @mentions +4. All participants can view and respond + +### Status Updates + +Track progress with status indicators: + +| Status | Icon | Meaning | +|--------|------|---------| +| Not Started | ○ | Task inactive or not begun | +| In Progress | ◐ | Work underway | +| Submitted | ◑ | Awaiting review | +| Approved | ● | Completed successfully | +| Rejected | ✕ | Returned for revision | +| Overdue | ⚠ | Past due date | + +--- + +## Best Practices + +### Process Design + +1. **Start simple** - Begin with linear workflows +2. **Test thoroughly** - Run pilot before full deployment +3. **Document clearly** - Detailed task descriptions +4. **Set realistic timelines** - Include buffer for delays + +### Task Configuration + +1. **Clear ownership** - One owner per task +2. **Appropriate assignees** - Match to data access +3. **Helpful work files** - Pre-configure filters +4. **Meaningful names** - Describe the action + +### Approval Workflows + +1. **Minimize levels** - Each level adds time +2. **Parallel when possible** - Review different data simultaneously +3. **Clear rejection criteria** - What requires rejection +4. **Escalation paths** - Handle non-responsive reviewers + +### Data Locking + +1. **Plan lock schedule** - Know when data closes +2. **Communicate clearly** - Inform users of deadlines +3. **Emergency unlock process** - Define for corrections +4. **Audit trail** - Track lock changes + +### Performance + +1. **Stagger task starts** - Avoid system overload +2. **Limit concurrent users** - On complex stories +3. **Background data actions** - For long-running operations +4. **Monitor process health** - Check for bottlenecks + +--- + +## Example: Complete Annual Budget Process + +### Process: Annual Budget 2025 + +**Duration**: January 1 - March 15 + +### Phase 1: Preparation (Jan 1-7) + +**Task 1.1**: Load Prior Year Actuals +- Type: Data Import Task +- Assignee: System Admin +- Duration: 1 day + +**Task 1.2**: Initialize Budget Versions +- Type: Multi Action Task +- Action: Create_Budget_Versions +- Predecessor: Task 1.1 + +### Phase 2: Data Entry (Jan 8-31) + +**Task 2.1**: Departmental Budget Entry +- Type: Composite Task +- Driving Dimension: Department +- Assignees: Department Heads +- Reviewer: Finance Manager +- Work File: Budget_Entry_Story +- Duration: 3 weeks + +### Phase 3: Review (Feb 1-21) + +**Task 3.1**: Finance Review +- Type: Review Task +- Reviewer: Finance Director +- Predecessor: Task 2.1 +- Duration: 1 week + +**Task 3.2**: Executive Review +- Type: Review Task +- Reviewer: CFO +- Predecessor: Task 3.1 +- Duration: 1 week + +### Phase 4: Finalization (Feb 22 - Mar 15) + +**Task 4.1**: Lock Budget Data +- Type: Data Locking Task +- Trigger: After Task 3.2 approval +- State: Locked + +**Task 4.2**: Generate Reports +- Type: General Task +- Assignee: Finance Analyst +- Predecessor: Task 4.1 + +**Task 4.3**: Board Presentation +- Type: General Task +- Assignee: CFO +- Due: March 15 + +--- + +**Documentation Links**: +- Calendar Overview: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/af4b7e39edd249d3b59fa7d4ab110a7a.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/af4b7e39edd249d3b59fa7d4ab110a7a.html) +- Multi-Level Approval: [https://blogs.sap.com/2020/07/10/multi-level-approval-in-sac-using-calendar-tasks/](https://blogs.sap.com/2020/07/10/multi-level-approval-in-sac-using-calendar-tasks/) +- Data Locking: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/e07d46e950794d5a928a9b16d1394de6.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/e07d46e950794d5a928a9b16d1394de6.html) +- Workflow Planning: [https://blogs.sap.com/2022/04/13/sap-analytics-cloud-workflow-planning/](https://blogs.sap.com/2022/04/13/sap-analytics-cloud-workflow-planning/) diff --git a/references/predictive-conversion.md b/references/predictive-conversion.md new file mode 100644 index 0000000..b3f5a6d --- /dev/null +++ b/references/predictive-conversion.md @@ -0,0 +1,14 @@ +# Predictive & Conversion Aids (SAC 2025.23) + +## Predictive forecasts in tables (007) +- Run time-series forecasts directly on table cells; choose measure/time scope; results written to target version. +- Good for quick, localized forecasts without full predictive step. + +## Currency & unit conversion (008) +- Plan with measures that carry currency/unit; conversion step in data actions can copy between measures with conversion settings. +- Key settings: Rate type (average/closing), Date, Category; choose overwrite vs append for target measure. + +## When to prefer multi action predictive step +- Need reusable scenario, background run, or integration with version publish; use multi action Predictive Step instead of table quick forecast. + +Sources: 007-predictive-forecasts-tables.md, 008-currency-unit-conversion.md (SAC Help 2025.23). diff --git a/references/s4hana-acdocp-export.md b/references/s4hana-acdocp-export.md new file mode 100644 index 0000000..8c2e7cb --- /dev/null +++ b/references/s4hana-acdocp-export.md @@ -0,0 +1,257 @@ +# Exporting SAC Native Planning Data to SAP S/4HANA ACDOCP + +Guide for exporting SAP Analytics Cloud native planning data to SAP S/4HANA ACDOCP table using the SAC Data Export Service. + +**Source**: [ZPARTNER - SAC Export Native Planning to SAP S/4HANA ACDOCP](https://www.zpartner.eu/sac-export-native-planning-to-sap-s-4hana-acdocp/) by Andreas Theodoridis (July 2025) + +--- + +## Table of Contents + +1. [Architecture Overview](#architecture-overview) +2. [Prerequisites](#prerequisites) +3. [Configuration Steps](#configuration-steps) +4. [Dimension Mapping Requirements](#dimension-mapping-requirements) +5. [Export Scope and Behavior](#export-scope-and-behavior) +6. [Troubleshooting](#troubleshooting) +7. [SAP Documentation Links](#sap-documentation-links) + +--- + +## Architecture Overview + +The integration uses the SAC Data Export Service to push planning data from SAC to S/4HANA's ACDOCP table (central storage for ERP plan data). + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ SAP Analytics Cloud │ +│ ┌───────────────────┐ ┌─────────────────────────────────────┐ │ +│ │ Native Planning │───>│ Data Export Service │ │ +│ │ Model │ │ (Data Management workspace) │ │ +│ └───────────────────┘ └──────────────┬──────────────────────┘ │ +└──────────────────────────────────────────┼──────────────────────────┘ + │ HTTPS + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ SAP Cloud Connector │ +│ (for on-premise S/4HANA) │ +└──────────────────────────────────────────┬───────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ SAP S/4HANA │ +│ ┌───────────────────────────────────────────────────────────────┐ │ +│ │ API_PLPACDOCPDATA_SRV (OData Service) │ │ +│ └──────────────────────────────────────┬────────────────────────┘ │ +│ │ │ +│ ┌───────────────────────────────────────▼───────────────────────┐ │ +│ │ ACDOCP Table (Plan Data Storage) │ │ +│ └───────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────┘ +``` + +**Required Components**: +1. SAC Native Planning Model +2. S/4HANA Connection (configured in SAC) +3. SAC Data Export Service (OData via Cloud Connector) +4. SAP S/4HANA ACDOCP table + +--- + +## Prerequisites + +### SAC Planning Model Configuration + +| Requirement | Details | +|-------------|---------| +| **Legacy Mode** | Must be enabled on the planning model (prerequisite for export) | +| **Version Dimension** | Required; only public versions can be exported | +| **Date Dimension** | Required (FiscalYearPeriod or Fiscal Year + Fiscal Period) | + +To enable Legacy Mode: +1. Open planning model in Modeler +2. Go to Model Preferences +3. Enable "Legacy Planning Model" option + +### S/4HANA Configuration + +| Component | Transaction/Action | +|-----------|-------------------| +| **OData Service** | Activate `API_PLPACDOCPDATA_SRV` in `/IWFND/MAINT_SERVICE` | +| **Master Data Services** | Additional OData services may be required for master data access | +| **Cloud Connector** | Configure for on-premise S/4HANA connectivity | + +### SAC Connection Setup + +Create an Import Data Connection to S/4HANA: +1. Go to Connections in SAC +2. Create new connection to S/4HANA system +3. Configure Cloud Connector settings (for on-premise) + +--- + +## Configuration Steps + +### Step 1: Create Data Export Job + +1. Open the planning model in SAC +2. Navigate to **Data Management** workspace +3. Click **Create** → **Data Export Job** +4. Select the configured S/4HANA OData endpoint + +### Step 2: Configure Dimension Mapping + +Map SAC dimensions to S/4HANA ACDOCP fields: + +| SAC Dimension | S/4HANA Field | Notes | +|---------------|---------------|-------| +| Version (Plan Category) | RVERS | Only public versions can be exported | +| Date (FiscalYearPeriod) | FISCPER / FISCYEAR + FISCPERIOD | Can use combined or separate fields | +| Account | RACCT | G/L Account (required) | +| Measure | Target Measure | Only ONE measure per export job | +| Cost Center | RCNTR | Must be linked to exported company code | +| Company Code | RBUKRS | Required for validation | + +**Important**: Only valid combinations of dimension members will be exported. For example, cost centers must be linked to the company code in the export scope. + +### Step 3: Set Filters + +Configure filters to define the export scope: +- Select source data (members to export) +- Set target data selection +- **Note**: Filters cannot be changed after export job creation—name jobs descriptively! + +### Step 4: Define Export Scope + +Select dimensions to include in export scope: +- **FiscalYearPeriod**: Mandatory +- **PlanningCategory (Version)**: Mandatory +- Additional dimensions as needed + +All members of marked dimensions will be overwritten if data is available. + +### Step 5: Schedule or Run Export + +- Run manually for testing +- Schedule for automated execution +- Name exports descriptively for tracking + +--- + +## Export Scope and Behavior + +### Overwrite Behavior + +Exported data **overwrites existing data within the defined scope**: + +``` +Export Scope Example: +- CostCenter: 17101101 +- FiscalYearPeriod: 2025 +- PlanningCategory: Plan + +Result: All Plan data for CostCenter 17101101 in 2025 is replaced +``` + +### Delta Records + +From a technical perspective, S/4HANA generates **delta records** when data changes: + +| Scenario | SAC Value | ACDOCP Result | +|----------|-----------|---------------| +| Initial export | 1000 | Record created: 1000 | +| Value changed | 1500 | Delta record: +500 (total: 1500) | +| Value set to 0 | 0 | Delta record: -1500 (total: 0) | + +### Deletion Behavior + +**Critical**: Deleting data in SAC does NOT delete data in ACDOCP: +- Deleted SAC values remain in ACDOCP until overwritten +- To remove values, set them to 0 in SAC and re-export +- Only exported data overwrites existing ACDOCP data + +--- + +## Troubleshooting + +### "Access Denied" for Master Data + +**Symptom**: Error when selecting master data in target data selection + +**Cause**: Missing OData services for master data access + +**Solution**: +1. Check OData error log: Transaction `/IWFND/ERROR_LOG` +2. Identify missing services from error details +3. Activate required OData services in `/IWFND/MAINT_SERVICE` + +### Field Mapping Errors + +**Symptom**: Data rejection during export + +**Cause**: Mismatches between SAC dimension values and S/4HANA valid combinations + +**Solution**: +1. Download error details file from export job +2. Verify dimension member combinations are valid in S/4HANA +3. Ensure cost centers are linked to correct company codes +4. Check G/L accounts exist in target ledger + +### Version Export Fails + +**Symptom**: Cannot select version for export + +**Cause**: Only public versions can be exported + +**Solution**: +1. Publish private version before export +2. Use standardized version names (e.g., PLAN, FORECAST) that exist in ACDOCP + +### Export Job Not Visible + +**Symptom**: Cannot find export option in Data Management + +**Cause**: Legacy mode not enabled + +**Solution**: Enable Legacy Planning Model in model preferences + +--- + +## SAP Documentation Links + +### Official SAP Resources + +| Resource | Link | +|----------|------| +| **KBA: Import/Export Job Checkpoints** | [SAP Note 3220268](https://userapps.support.sap.com/sap/support/knowledge/en/3220268) (S-User required) | +| **Cloud Connector Setup** | [SAP Help - Import Data Connection to S/4HANA](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/63140f17362947fe8bcd9c6960db23bc.html) | +| **Exporting Plan Data to S/4HANA** | [SAP Help - Export Planning Data](https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE_UPA/e9090901b1e24f4d9d04f6f206abecd8/efee7f7a47844876a80565d50f1cffcd.html) | +| **OData Services for Export** | [SAP Help - OData Services](https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE_UPA/e9090901b1e24f4d9d04f6f206abecd8/15447e5bda934be28f3b16cfb456fb23.html) | + +### Quick Reference + +``` +Key OData Service: API_PLPACDOCPDATA_SRV +Error Log Transaction: /IWFND/ERROR_LOG +Service Activation: /IWFND/MAINT_SERVICE +ACDOCP Verification: SE16N +``` + +--- + +## Summary + +| Aspect | Key Point | +|--------|-----------| +| **Prerequisites** | Legacy mode ON, API_PLPACDOCPDATA_SRV activated | +| **Version Requirement** | Only public versions can be exported | +| **Measure Limit** | One target measure per export job | +| **Mandatory Scope** | FiscalYearPeriod + PlanningCategory always required | +| **Overwrite Behavior** | Exported data replaces existing within scope | +| **Deletions** | Set to 0 and re-export; deletions don't propagate automatically | + +--- + +**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/scheduling-calendar.md b/references/scheduling-calendar.md new file mode 100644 index 0000000..0a808c1 --- /dev/null +++ b/references/scheduling-calendar.md @@ -0,0 +1,29 @@ +# Scheduling & Calendar Tasks (SAC 2025.23) + +Purpose: schedule structured planning operations (data actions, multi actions, data locks) and coordinate human tasks via the SAC calendar. + +## When to schedule +- Need unattended runs in off-hours or after data loads. +- Need predictable cadence with approvals or dependencies. +- Want to chain automated steps with review/input tasks. + +## Task types to use +- **Automatic Data Action Task**: schedule a data action with parameter prompts; runs in background; respects planning area if public version in edit mode (031-schedule-data-actions-calendar.md). +- **Automatic Multi Action Task**: schedule multi action; good for cross-model orchestration; can reuse parameters. +- **Data Locking Task**: set lock states on a schedule (e.g., close months). +- **Input/Review/Composite Tasks**: human workflow; assign owners/reviewers; attach files and due dates. + +## Setup checklist +- Confirm starter access + Execute permission on underlying action(s); write access to member scope per data access control. +- For public versions, ensure edit mode / recommended planning area defined before scheduling. +- Define recurrence and end date; align time zone with planners. +- Attach predecessors/successors to enforce order; use notifications for assignees. + +## Background run behavior +- Users can continue working, but the same version is locked for changes until run completes; refresh to see results. +- Status + messages appear in Notifications and Job Monitor. + +## Approvals & handoffs +- Use review tasks for sign-off; composite tasks to bundle multiple steps; dependencies ensure gating before publish. + +Related: `references/job-monitoring.md` for tracking; `references/multi-actions.md` for step design. diff --git a/references/version-edit-modes.md b/references/version-edit-modes.md new file mode 100644 index 0000000..6b03a7f --- /dev/null +++ b/references/version-edit-modes.md @@ -0,0 +1,22 @@ +# Version Edit Modes & Publishing (SAC 2025.23) + +## Modes +- **Public version (view)**: read-only until edit started. +- **Edit Mode (planning area)**: creates a temporary private copy scoped to planning area; locks same scope for others. +- **Private versions**: user-only until shared/published; good for simulations. + +## Running actions on versions +- Data/multi actions on public versions run only inside the planning area (or recommended planning area if enabled) when edit mode is active. +- To affect full public data, publish first or widen planning area before run. +- Background runs still obey planning area scope. + +## Publishing options (multi action or manual) +- **Publish and fail on warnings**: stops if locks/restrictions encountered. +- **Publish and ignore warnings**: publishes allowed data; drops blocked records. +- **Publish As**: create new public version from private. + +## Good practice +- Use private versions for what-if; publish only after validation. +- Coordinate planning area scope with scheduled actions to avoid partial updates. + +Related: `references/multi-actions.md` (publish steps) and `references/data-actions.md` (write modes). diff --git a/references/version-management.md b/references/version-management.md new file mode 100644 index 0000000..3af964b --- /dev/null +++ b/references/version-management.md @@ -0,0 +1,495 @@ +# SAP Analytics Cloud - Version Management Reference + +**Sources**: 012-version-management-overview.md (SAC 2025.23) + related SAP Help portal pages +**Last Updated**: 2025-11-25 + +--- + +## Table of Contents + +1. [Version Types Overview](#version-types-overview) +2. [Public Versions](#public-versions) +3. [Private Versions](#private-versions) +4. [Edit Mode](#edit-mode) +5. [Publishing Workflows](#publishing-workflows) +6. [Version API](#version-api) +7. [Best Practices](#best-practices) + +--- + +## Version Types Overview + +SAP Analytics Cloud uses versions to manage different views of planning data. + +### Version Dimension + +Planning models require a **Version** dimension with: +- At least one public version (e.g., Actual, Budget, Forecast) +- System-managed private versions per user + +### Comparison + +| Aspect | Public Version | Private Version | +|--------|----------------|-----------------| +| Visibility | All users with access | Creator only (unless shared) | +| Persistence | Permanent until deleted | Temporary (deleted when published/reverted) | +| Use Case | Official data | Draft, simulation, what-if | +| Editing | Creates edit mode | Direct editing | +| Publishing | Target of publish | Source of publish | + +--- + +## Public Versions + +Shared versions visible to all users with model access. + +### Characteristics + +- **Permanent**: Data persists until explicitly deleted +- **Shared**: Same data for all users +- **Controlled**: Changes require publish workflow +- **Reportable**: Used in official reports and dashboards + +### Common Public Versions + +| Version | Purpose | +|---------|---------| +| Actual | Historical data from source systems | +| Budget | Approved annual budget | +| Forecast | Rolling forecast updates | +| Plan | Working plan version | +| Prior_Year | Previous year comparison | + +### Creating Public Versions + +**From UI**: +1. Open Version Management panel +2. Click **Create Version** +3. Select **Public** +4. Enter ID and description +5. Choose to copy from existing or start blank + +**Via Data Action**: +``` +Step Type: Copy +Source: Existing version with data +Target: New version ID +``` + +### Deleting Public Versions + +**Requirements**: +- Version must be empty (no data) +- User must have delete permission +- Version not in use by running processes + +**Via UI**: +1. Open Version Management +2. Select version +3. Click **Delete** (only shows if empty) + +--- + +## Private Versions + +User-specific versions for draft work and simulations. + +### Characteristics + +- **Personal**: Only creator can see (unless shared) +- **Temporary**: Designed for draft work +- **Flexible**: Direct editing without publish +- **Isolated**: Changes don't affect others + +### Creating Private Versions + +**Method 1: Copy from Public** +1. Open Version Management +2. Select public version +3. Click **Copy to Private** +4. Enter name and description + +**Method 2: Via API** +```javascript +// Get private version (creates if needed when editing) +var privateVersion = Table_1.getPlanning().getPrivateVersion(); +``` + +**Method 3: Auto-Created** +When editing a public version, SAC automatically creates a private edit mode. + +### Private Version Properties + +| Property | Description | +|----------|-------------| +| ID | System-generated or user-defined | +| Description | User-provided description | +| Source Version | Public version it was copied from | +| Created Date | When private version was created | +| Last Modified | Most recent edit timestamp | + +### Sharing Private Versions + +Share with colleagues for collaboration: + +1. Open Version Management → Private Versions +2. Select version → **More (...)** → **Share** +3. Choose access level: + - **Read Only**: View but not edit + - **Read and Write**: Full editing access +4. Select users or teams + +### Private Version Limits + +- System limit on number of private versions per user +- Inactive versions may be auto-cleaned +- Check tenant settings for specific limits + +--- + +## Edit Mode + +When editing a public version, SAC creates a temporary private copy. + +### How Edit Mode Works + +``` +User starts editing public version + ↓ +System creates "edit mode" (private copy) + ↓ +User makes changes (only they can see) + ↓ +User publishes OR reverts + ↓ +Edit mode merged to public OR discarded +``` + +### Entering Edit Mode + +**Automatic**: Edit any cell in a planning-enabled table on public version + +**Via UI**: +1. Version Management → Public Versions +2. Select version → **Edit** + +### Edit Mode Indicators + +- Table shows "Edit Mode" indicator +- Version selector shows edit status +- Other users see original public data + +### Exiting Edit Mode + +**Publish**: Merge changes to public version +**Revert**: Discard all changes +**Auto-Discard**: After inactivity timeout + +### Concurrent Editing + +- Multiple users can have edit mode on same public version +- Each user's changes isolated until publish +- Conflicts resolved at publish time: + - Last write wins for same cells + - Different cells merge cleanly + +--- + +## Publishing Workflows + +### Publish (Merge to Source) + +Merges private/edit mode changes back to original public version. + +**Steps**: +1. Complete data entry in private version +2. Click **Publish** in Version Management +3. System validates changes +4. Valid changes merge to public +5. Private version/edit mode deleted + +**Validation Rules**: +- Data access control (user can write to cells) +- Data locks (cells not locked) +- Validation rules (pass model rules) + +**Important**: Invalid changes are discarded, not merged. + +### Publish As (Create New Public) + +Creates a new public version from private data. + +**Steps**: +1. Complete data entry in private version +2. Click **Publish As** in Version Management +3. Enter new version ID and description +4. System creates new public version +5. Private version optionally deleted + +**Use Cases**: +- Create new budget version from draft +- Save simulation as official scenario +- Archive point-in-time snapshot + +### Revert (Discard Changes) + +Discards all changes in private version/edit mode. + +**Steps**: +1. Open Version Management +2. Select private version or edit mode +3. Click **Revert** +4. Confirm discarding changes +5. Private version/edit mode deleted + +**When to Revert**: +- Started over with wrong assumptions +- Discovered errors requiring fresh start +- Simulation no longer needed + +--- + +## Version API + +### getPlanning() Version Methods + +```javascript +// Get all public versions +var publicVersions = Table_1.getPlanning().getPublicVersions(); + +// Get specific public version +var budget = Table_1.getPlanning().getPublicVersion("Budget_2025"); + +// Get current private version +var privateVer = Table_1.getPlanning().getPrivateVersion(); + +// Get all user's private versions +var allPrivate = Table_1.getPlanning().getPrivateVersions(); +``` + +### PlanningVersion Object + +```javascript +// Version properties +console.log(version.id); // "Budget_2025" +console.log(version.description); // "Annual Budget 2025" + +// Check for unsaved changes +if (version.isDirty()) { + console.log("Has unpublished changes"); +} + +// Publish changes +version.publish(); + +// Publish as new version +version.publishAs("Budget_2025_v2", "Revised Budget 2025"); + +// Revert changes +version.revert(); +``` + +### Version Selection in Data Source + +```javascript +// Filter to specific version +Table_1.getDataSource().setDimensionFilter("Version", + "[Version].[parentId].&[public.Budget_2025]"); + +// Get current version filter +var versionFilter = Table_1.getDataSource().getDimensionFilters("Version"); +``` + +### Version-Based Workflow Example + +```javascript +// Complete workflow: create private, edit, publish +function createBudgetVersion(sourceVersion, targetVersion) { + Application.showBusyIndicator(); + + // 1. Copy source to private + var source = Table_1.getPlanning().getPublicVersion(sourceVersion); + // (User would edit data in table) + + // 2. Get private version after editing + var privateVer = Table_1.getPlanning().getPrivateVersion(); + + // 3. Publish as new version + if (privateVer && privateVer.isDirty()) { + privateVer.publishAs(targetVersion, "Created from " + sourceVersion); + Application.showMessage("Version " + targetVersion + " created"); + } + + Application.hideBusyIndicator(); +} +``` + +--- + +## Version Management Panel + +### Accessing the Panel + +**In Story**: +1. Open planning-enabled story +2. Click **Version Management** in toolbar + +**In Analytics Designer**: +1. Add Version Management panel programmatically +2. Or create custom UI with API calls + +### Panel Sections + +**Public Versions**: +- List all public versions +- Create, edit, delete options +- Publish target selection + +**Private Versions**: +- User's private versions +- Publish, share, delete options +- Copy from public option + +**Edit Mode**: +- Current edit mode status +- Publish or revert options +- Time since last save + +### Version Operations + +| Operation | Availability | Requirements | +|-----------|-------------|--------------| +| Create Public | Public section | Model write access | +| Delete Public | Public section | Empty version, delete permission | +| Edit Public | Public section | Write access | +| Copy to Private | Public section | Read access | +| Publish | Private/Edit | Write access to target | +| Publish As | Private section | Create version permission | +| Share Private | Private section | Share permission | +| Revert | Private/Edit | None (own changes only) | + +--- + +## Best Practices + +### Version Naming Conventions + +``` +Public Versions: + Actual - Historical/imported data + Budget_YYYY - Annual budget by year + Forecast_YYYY_QN - Quarterly forecast + Plan_YYYY_MM - Monthly plan version + +Private Versions: + [User]_Draft_[Date] - Personal drafts + WhatIf_[Scenario] - Simulation scenarios +``` + +### Version Lifecycle + +``` +1. Planning Cycle Starts + └── Create new Budget_YYYY version (empty or seeded) + +2. Data Entry Phase + └── Users work in private versions + └── Submit for review + +3. Review Phase + └── Reviewers check private versions + └── Approve or reject + +4. Finalization + └── Publish approved to public + └── Lock public version + └── Clean up private versions + +5. Reporting + └── Use public version for reports + └── Compare to Actual version +``` + +### Performance Considerations + +1. **Limit active versions** - Delete unused versions +2. **Clean private versions** - Encourage users to publish or revert +3. **Use version filters** - Don't load all versions in reports +4. **Archive old versions** - Export and delete historical versions + +### Security Recommendations + +1. **Version-level access** - Control who can see/edit each version +2. **Audit changes** - Track who published what +3. **Backup before delete** - Export version data first +4. **Lock approved versions** - Prevent accidental changes + +### Common Mistakes to Avoid + +| Mistake | Solution | +|---------|----------| +| Too many private versions | Set cleanup policy, encourage publish | +| Orphaned edit modes | Auto-timeout after inactivity | +| Conflicting publishes | Communicate publishing schedule | +| Deleting needed versions | Implement approval for deletion | +| Version naming chaos | Enforce naming conventions | + +--- + +## Troubleshooting + +### "Cannot publish - validation failed" + +**Causes**: +- Data access control blocking cells +- Data lock on target cells +- Model validation rules failing + +**Solution**: +- Check data access permissions +- Verify lock status +- Review validation rule messages + +### "Version not appearing" + +**Causes**: +- No access to version +- Filter hiding version +- Version recently deleted + +**Solution**: +- Check version permissions +- Remove version filters +- Refresh model metadata + +### "Edit mode lost" + +**Causes**: +- Session timeout +- System cleanup +- Another user published + +**Solution**: +- Save frequently +- Check auto-save settings +- Coordinate with other users + +### "Cannot delete version" + +**Causes**: +- Version contains data +- Version in use by process +- Insufficient permissions + +**Solution**: +- Clear version data first +- Complete or cancel processes +- Request delete permission + +--- + +**Documentation Links**: +- Version Management: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/9d9056a13b764ad3aca8fef2630fcc00.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/9d9056a13b764ad3aca8fef2630fcc00.html) +- Public Versions: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/b6e3d093988e4c3eba7eb6c1c110e954.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/b6e3d093988e4c3eba7eb6c1c110e954.html) +- Private Versions: [https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/1a011f8041a84e109a3b6bf8c1c81bc1.html](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/00f68c2e08b941f081002fd3691d86a7/1a011f8041a84e109a3b6bf8c1c81bc1.html) +- Sharing Versions: [https://help.sap.com/doc/00f68c2e08b941f081002fd3691d86a7/2023.20/en-US/e763e27e0f4c419488381f77937a0ff1.html](https://help.sap.com/doc/00f68c2e08b941f081002fd3691d86a7/2023.20/en-US/e763e27e0f4c419488381f77937a0ff1.html) diff --git a/references/version-publishing-notes.md b/references/version-publishing-notes.md new file mode 100644 index 0000000..7376cb6 --- /dev/null +++ b/references/version-publishing-notes.md @@ -0,0 +1,9 @@ +# Version Publishing Notes (SAC 2025.23) + +- Publish/Publish As can be initiated from Version Management, data action publish settings, or multi action version steps (choose fail vs ignore warnings). +- Invalid or locked records are dropped during publish; valid records merge. +- Planning area: when a public version is in edit mode with a planning area (or recommended planning area), data/multi actions only affect that scope. Expand planning area or publish first for full coverage; background runs respect scope. +- Use Publish As to branch scenarios without overwriting baseline; rename clearly. +- Always refresh story/app after background publishes to view merged data. + +Sources: 012-version-management-overview.md and related SAC Help pages (2025.23). diff --git a/templates/data-action-checklist.md b/templates/data-action-checklist.md new file mode 100644 index 0000000..a069a60 --- /dev/null +++ b/templates/data-action-checklist.md @@ -0,0 +1,10 @@ +# Data Action Design Checklist + +- [ ] Target model + version selected (public vs private) and edit mode/planning area confirmed. +- [ ] Step order defined (copy/cross-copy → allocations → conversions → advanced formulas → embedded actions). +- [ ] Parameters set (member/number/measure) with defaults; prompts labeled clearly. +- [ ] Copy/aggregation rules map to leaf members; non-leaf booking avoided. +- [ ] Write mode chosen per step (append vs overwrite) and currency/unit conversions configured. +- [ ] Validation run on small slice (time/account filters) before full run. +- [ ] Background run settings decided; notifications/monitor path shared with planners. +- [ ] Error handling notes captured (locks, mapping gaps) in job monitor playbook. diff --git a/templates/multi-action-checklist.md b/templates/multi-action-checklist.md new file mode 100644 index 0000000..f09d03f --- /dev/null +++ b/templates/multi-action-checklist.md @@ -0,0 +1,10 @@ +# Multi Action Design Checklist + +- [ ] Confirm use-case needs sequencing across models/versions; partial success acceptable. +- [ ] Steps chosen: data action / version publish / predictive / data import / API / PaPM / data locking. +- [ ] Parameters defined (including cross-model) and mapped to each step; defaults provided. +- [ ] Publishing behavior set (fail vs ignore warnings) per step; planning area option reviewed. +- [ ] Background execution allowed; users notified of version lock window and refresh need. +- [ ] Slicing strategy for long runs (filters, smaller steps) to avoid timeouts. +- [ ] Scheduled? Add calendar task with recurrence/time zone + dependencies. +- [ ] Job monitor path documented for operators; fallback manual steps noted. diff --git a/templates/parameter-table.md b/templates/parameter-table.md new file mode 100644 index 0000000..ea679fd --- /dev/null +++ b/templates/parameter-table.md @@ -0,0 +1,12 @@ +# Parameter Mapping Template + +| ID | Type (Number/Member/Measure/String/DateTime) | Model/Dimension (if member) | Level (Any/Leaf) | Cardinality (Single/Any) | Default | Prompt Label | Used In (step) | +|----|---------------------------------------------|-----------------------------|------------------|---------------------------|---------|--------------|----------------| +| | | | | | | | | +| | | | | | | | | +| | | | | | | | | + +Usage notes: +- For cross-model parameters, ensure dimension is public and shared; reuse across steps with same dimension. +- Keep Level=Leaf when targeting booking destinations; allow Any when used for filters/prompts. +- Sort prompts in data action designer to present critical parameters first.