Initial commit
This commit is contained in:
587
templates/planning-operations.js
Normal file
587
templates/planning-operations.js
Normal file
@@ -0,0 +1,587 @@
|
||||
/**
|
||||
* SAP Analytics Cloud - Planning Operations Patterns
|
||||
*
|
||||
* Ready-to-use code patterns for SAC Planning applications.
|
||||
* Copy and adapt these patterns to your application.
|
||||
*
|
||||
* Source: SAP Analytics Cloud Scripting Skill
|
||||
* Version: 2025.14+
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// VERSION MANAGEMENT PATTERNS
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Pattern 1: Get and display version info
|
||||
* Use in: Application initialization or info button
|
||||
*/
|
||||
function displayVersionInfo() {
|
||||
var planning = Table_1.getPlanning();
|
||||
|
||||
var publicVersions = planning.getPublicVersions();
|
||||
var privateVersions = planning.getPrivateVersions();
|
||||
|
||||
console.log("Public Versions:", publicVersions.length);
|
||||
console.log("Private Versions:", privateVersions.length);
|
||||
|
||||
publicVersions.forEach(function(v) {
|
||||
console.log("Public: " + v.id);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pattern 2: Publish version with validation
|
||||
* Use in: Publish button onClick
|
||||
*/
|
||||
function publishVersion(versionId) {
|
||||
Application.showBusyIndicator();
|
||||
|
||||
try {
|
||||
var version = Table_1.getPlanning().getPublicVersion(versionId);
|
||||
|
||||
if (!version) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Version '" + versionId + "' not found"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!version.isDirty()) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"No changes to publish"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
version.publish();
|
||||
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Success,
|
||||
"Version published successfully"
|
||||
);
|
||||
|
||||
// Refresh data
|
||||
Table_1.getDataSource().refreshData();
|
||||
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.log("Publish error:", error);
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Failed to publish: " + error
|
||||
);
|
||||
return false;
|
||||
|
||||
} finally {
|
||||
Application.hideBusyIndicator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pattern 3: Copy public version to new private version
|
||||
* Use in: Create working copy button
|
||||
*/
|
||||
function createWorkingCopy(sourceVersionId) {
|
||||
Application.showBusyIndicator();
|
||||
|
||||
try {
|
||||
var sourceVersion = Table_1.getPlanning().getPublicVersion(sourceVersionId);
|
||||
|
||||
if (!sourceVersion) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Source version not found"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
var timestamp = Date.now().toString();
|
||||
var newVersionId = "WorkingCopy_" + timestamp;
|
||||
|
||||
sourceVersion.copy(newVersionId, PlanningCopyOption.AllData);
|
||||
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Success,
|
||||
"Working copy created: " + newVersionId
|
||||
);
|
||||
|
||||
return newVersionId;
|
||||
|
||||
} catch (error) {
|
||||
console.log("Copy error:", error);
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Failed to create copy"
|
||||
);
|
||||
return null;
|
||||
|
||||
} finally {
|
||||
Application.hideBusyIndicator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pattern 4: Copy with specific category (Budget/Forecast)
|
||||
* Use in: Category-specific copy operations
|
||||
*/
|
||||
function copyAsCategory(sourceVersionId, targetVersionId, category) {
|
||||
Application.showBusyIndicator();
|
||||
|
||||
try {
|
||||
var sourceVersion = Table_1.getPlanning().getPrivateVersion(sourceVersionId);
|
||||
|
||||
if (!sourceVersion) {
|
||||
sourceVersion = Table_1.getPlanning().getPublicVersion(sourceVersionId);
|
||||
}
|
||||
|
||||
if (!sourceVersion) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Source version not found"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy with category
|
||||
sourceVersion.copy(
|
||||
targetVersionId,
|
||||
PlanningCopyOption.AllData,
|
||||
category // PlanningCategory.Budget or PlanningCategory.Forecast
|
||||
);
|
||||
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Success,
|
||||
"Version copied successfully"
|
||||
);
|
||||
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.log("Copy error:", error);
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Failed to copy version"
|
||||
);
|
||||
return false;
|
||||
|
||||
} finally {
|
||||
Application.hideBusyIndicator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pattern 5: Revert unsaved changes
|
||||
* Use in: Discard changes button
|
||||
*/
|
||||
function revertChanges(versionId) {
|
||||
var version = Table_1.getPlanning().getPublicVersion(versionId);
|
||||
|
||||
if (!version) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Version not found"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!version.isDirty()) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"No changes to revert"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
version.revert();
|
||||
Table_1.getDataSource().refreshData();
|
||||
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"Changes reverted"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pattern 6: Delete private version
|
||||
* Use in: Delete version button (with confirmation)
|
||||
*/
|
||||
function deletePrivateVersion(versionId) {
|
||||
var version = Table_1.getPlanning().getPrivateVersion(versionId);
|
||||
|
||||
if (!version) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Private version not found"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
version.delete();
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Success,
|
||||
"Version deleted"
|
||||
);
|
||||
return true;
|
||||
} catch (error) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Failed to delete version"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// DATA LOCKING PATTERNS
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Pattern 7: Check lock state before edit
|
||||
* Use before: Any data modification
|
||||
*/
|
||||
function checkLockState() {
|
||||
var selections = Table_1.getSelections();
|
||||
|
||||
if (selections.length === 0) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Warning,
|
||||
"Please select a cell first"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
var lockState = Table_1.getPlanning()
|
||||
.getDataLocking()
|
||||
.getState(selections[0]);
|
||||
|
||||
return lockState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pattern 8: Lock state validation with message
|
||||
* Use in: Edit button onClick
|
||||
*/
|
||||
function validateAndEdit() {
|
||||
var lockState = checkLockState();
|
||||
|
||||
if (lockState === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (lockState) {
|
||||
case DataLockingState.Locked:
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Warning,
|
||||
"Data is locked by another user. Cannot edit."
|
||||
);
|
||||
break;
|
||||
|
||||
case DataLockingState.LockedByMe:
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"Data is locked by you. Proceeding with edit."
|
||||
);
|
||||
performEdit();
|
||||
break;
|
||||
|
||||
case DataLockingState.Unlocked:
|
||||
performEdit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function performEdit() {
|
||||
// Implement edit logic
|
||||
console.log("Performing edit...");
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// DATA ACTION PATTERNS
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Pattern 9: Execute data action with parameters
|
||||
* Use in: Run data action button
|
||||
*/
|
||||
function runDataAction(year, version) {
|
||||
Application.showBusyIndicator();
|
||||
|
||||
// Set parameters
|
||||
DataAction_Allocation.setParameterValue("YEAR", year);
|
||||
DataAction_Allocation.setParameterValue("VERSION", version);
|
||||
|
||||
// Execute
|
||||
DataAction_Allocation.execute();
|
||||
}
|
||||
|
||||
// Data action event handlers
|
||||
DataAction_Allocation.onBeforeExecute = function() {
|
||||
console.log("Data action starting...");
|
||||
};
|
||||
|
||||
DataAction_Allocation.onAfterExecute = function(result) {
|
||||
Application.hideBusyIndicator();
|
||||
|
||||
if (result.success) {
|
||||
Table_1.getDataSource().refreshData();
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Success,
|
||||
"Data action completed successfully"
|
||||
);
|
||||
} else {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Data action failed: " + (result.message || "Unknown error")
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Pattern 10: Data action with dropdown parameters
|
||||
* Use in: Dynamic data action execution
|
||||
*/
|
||||
function runDataActionFromDropdowns() {
|
||||
var sourceVersion = Dropdown_SourceVersion.getSelectedKey();
|
||||
var targetVersion = Dropdown_TargetVersion.getSelectedKey();
|
||||
var year = Dropdown_Year.getSelectedKey();
|
||||
|
||||
if (!sourceVersion || !targetVersion || !year) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Warning,
|
||||
"Please select all parameters"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Application.showBusyIndicator();
|
||||
|
||||
DataAction_Copy.setParameterValue("SOURCE_VERSION", sourceVersion);
|
||||
DataAction_Copy.setParameterValue("TARGET_VERSION", targetVersion);
|
||||
DataAction_Copy.setParameterValue("YEAR", year);
|
||||
|
||||
DataAction_Copy.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pattern 11: Background data action execution
|
||||
* Use for: Long-running data actions
|
||||
*/
|
||||
function runDataActionInBackground() {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"Data action started in background"
|
||||
);
|
||||
|
||||
DataAction_LongRunning.executeInBackground();
|
||||
}
|
||||
|
||||
DataAction_LongRunning.onExecutionStatusUpdate = function(status) {
|
||||
console.log("Background status:", status);
|
||||
|
||||
switch (status) {
|
||||
case ExecutionStatus.Running:
|
||||
console.log("Still running...");
|
||||
break;
|
||||
|
||||
case ExecutionStatus.Completed:
|
||||
Table_1.getDataSource().refreshData();
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Success,
|
||||
"Background task completed"
|
||||
);
|
||||
break;
|
||||
|
||||
case ExecutionStatus.Failed:
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Background task failed"
|
||||
);
|
||||
break;
|
||||
|
||||
case ExecutionStatus.Cancelled:
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"Background task cancelled"
|
||||
);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// PLANNING WORKFLOW PATTERNS
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Pattern 12: Complete planning workflow
|
||||
* Create copy → Edit → Publish
|
||||
*/
|
||||
var WorkflowState = {
|
||||
workingVersion: null
|
||||
};
|
||||
|
||||
// Step 1: Create working copy
|
||||
function startPlanningWorkflow(sourceVersionId) {
|
||||
var newVersionId = createWorkingCopy(sourceVersionId);
|
||||
if (newVersionId) {
|
||||
WorkflowState.workingVersion = newVersionId;
|
||||
Table_1.getDataSource().setDimensionFilter("Version", newVersionId);
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Info,
|
||||
"Editing version: " + newVersionId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Save progress
|
||||
function savePlanningProgress() {
|
||||
if (!WorkflowState.workingVersion) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Warning,
|
||||
"No active working version"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Table_1.getPlanning().submitData();
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Success,
|
||||
"Changes saved"
|
||||
);
|
||||
}
|
||||
|
||||
// Step 3: Complete workflow
|
||||
function completePlanningWorkflow() {
|
||||
if (!WorkflowState.workingVersion) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Warning,
|
||||
"No active working version"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var version = Table_1.getPlanning().getPrivateVersion(WorkflowState.workingVersion);
|
||||
if (version && version.isDirty()) {
|
||||
version.publish();
|
||||
}
|
||||
|
||||
WorkflowState.workingVersion = null;
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Success,
|
||||
"Planning workflow completed"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pattern 13: Find active planning cycle from attribute
|
||||
* Use in: Auto-filter to current planning cycle
|
||||
*/
|
||||
function findActivePlanningCycle() {
|
||||
var allCycles = PlanningModel_1.getMembers("PlanningCycle");
|
||||
|
||||
for (var i = 0; i < allCycles.length; i++) {
|
||||
// Check for active flag attribute
|
||||
if (allCycles[i].properties.IsActive === "X" ||
|
||||
allCycles[i].properties.Flag === "PC+0") {
|
||||
return allCycles[i].id;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pattern 14: Set year filter based on planning cycle
|
||||
* Use in: Dynamic year filtering
|
||||
*/
|
||||
function setYearFromPlanningCycle() {
|
||||
var activeCycle = findActivePlanningCycle();
|
||||
|
||||
if (activeCycle) {
|
||||
// Format the filter value
|
||||
var yearFilter = "[Date].[YQM].&[" + activeCycle + "]";
|
||||
|
||||
Table_1.getDataSource().setDimensionFilter("Date", yearFilter);
|
||||
|
||||
console.log("Active planning cycle set:", activeCycle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// INPUT FORM PATTERNS
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Pattern 15: Submit data after input
|
||||
* Use in: Save button for input forms
|
||||
*/
|
||||
function submitInputData() {
|
||||
Application.showBusyIndicator();
|
||||
|
||||
try {
|
||||
// Submit pending changes
|
||||
Table_1.getPlanning().submitData();
|
||||
|
||||
// Get current version and publish
|
||||
var version = Table_1.getPlanning().getPublicVersion("Budget2024");
|
||||
if (version && version.isDirty()) {
|
||||
version.publish();
|
||||
}
|
||||
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Success,
|
||||
"Data submitted successfully"
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
console.log("Submit error:", error);
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Failed to submit data"
|
||||
);
|
||||
} finally {
|
||||
Application.hideBusyIndicator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pattern 16: Validate before submit
|
||||
* Use in: Pre-submission validation
|
||||
*/
|
||||
function validateAndSubmit() {
|
||||
// Check for required selections
|
||||
if (!Dropdown_CostCenter.getSelectedKey()) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Warning,
|
||||
"Please select a cost center"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check lock state
|
||||
var selections = Table_1.getSelections();
|
||||
if (selections.length > 0) {
|
||||
var lockState = Table_1.getPlanning()
|
||||
.getDataLocking()
|
||||
.getState(selections[0]);
|
||||
|
||||
if (lockState === DataLockingState.Locked) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Error,
|
||||
"Data is locked. Cannot submit."
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Proceed with submit
|
||||
submitInputData();
|
||||
}
|
||||
Reference in New Issue
Block a user