Data and Analytics Blog Posts
cancel
Showing results for 
Search instead for 
Did you mean: 
yash16jadhav47
Discoverer
96

Business Scenario / Problem Statement

Long-range forecasting often needs to plan for things that haven't happened yet — for example, revenue for customers a company intends to onboard in the future. Since these customers don't exist in the planning master data yet, users need a way to create them directly, along with control over attributes like their planning status.

This gave rise to two problems I needed to solve:

  1. Creating new master data on the fly: business users needed a controlled way to add a new customer to the planning model dimension and maintain attributes like Customer Status (Active/Inactive for planning) whenever required — without needing broader access to the planning model itself.
  2. Auto-incrementing the Customer ID: since customer IDs follow a simple numeric sequence, manually asking users to key in the next available ID is error-prone and invites duplicates. The form needed to work out the next ID on its own each time it loads.

With the help of SACs inbuilt scripting capabilities — it gives a limited set of business users a simple, governed way to create a new planning-only customer record with an auto-generated ID, and to update attributes on that record later.

Solution Overview

I built a two-part Story using SAC's Advanced Mode scripting capabilities:

  • Create Customer Master Data — auto-generates the next available Customer ID, captures description Customer Name, and attributes UAR Number, Customer Status, and UAR Status, and writes a new member into the Customer dimension.
  • Update Customer Master Data — lets a user pick an existing (including previously self-created) customer from a dropdown and update just the Customer Status attribute.

Both forms use the SAC Planning Model scripting APIs getMembers, getMember, createMembers, and updateMembers against the Customer dimension of the planning model, all scripted directly on the widgets within the Story canvas.

Screenshot 2026-07-05 195159.png

Script Variables

A handful of script variables drive the whole application:

VariableTypePurpose
allmemberPlanningModelMemberHolds the JSON payload passed to createMembers / updateMembers
customerIdFilterValueReference to the customer being worked on
NewCustomerNumberNumberNumeric form of the next available Customer ID, used for the increment logic
NewCustomerStringStringString form of NewCustomerNumber, since dimension member IDs are strings
 

Walkthrough:

OnInitialization of page event:

When the page loads, the same block of logic serves both forms:

Application.showBusyIndicator("Initializing new customer...");
var customerDim = PlanningModel_1.getMembers("Customer");

For the Update form, the dropdown is rebuilt from scratch and seeded with a placeholder instruction:

Dropdown_1.removeAllItems();
Dropdown_1.addItem("Select a Customer", "Select a Customer");
Dropdown_1.setSelectedKey("Select a Customer");

for (var i = 1; i < customerDim.length; i++) {
    Dropdown_1.addItem(customerDim[i].id, customerDim[i].description);
}

Starting the loop at index 1 skips the technical "not assigned" default member that most dimensions carry at index 0 — a small but important detail if you don't want that showing up as a selectable customer.

For the Create form, the trick is generating the next Customer ID automatically. Since new customers are always appended with an incrementing numeric ID, the last entry in the dimension is always the highest existing ID:

var LatestEntry = customerDim[customerDim.length - 1].id;
NewCustomerNumber = ConvertUtils.stringToNumber(LatestEntry) + 1;
NewCustomerString = ConvertUtils.numberToString(NewCustomerNumber);
Text_9.applyText(NewCustomerString);

The ID needs to round-trip between number and string because incrementing a string doesn't work the way you'd want ("740" + 1 isn't "741" in JavaScript), but the dimension member ID itself has to be written back as a string.

OnClick event of Button (Submit) - Creating a New Customer:

On submit of the create form, the values entered in the input fields and radio button groups are packaged into a single member object and pushed to the model:

var custName = InputField_2.getValue().trim();
var UARNo = InputField_3.getValue().trim();
var CustomerStatus = RadioButtonGroup_1.getSelectedKey();
var UARStatus = RadioButtonGroup_2.getSelectedKey();

allmember = ({
  id: NewCustomerString,
  description: custName,
  properties: { UAR_NO: UARNo, CustomerStatus: CustomerStatus, UAR_Status: UARStatus }
});

PlanningModel_1.createMembers("Customer", allmember);

Once the new member is created, the form resets itself and — this is the part I like — increments the counter again and re-populates the next available Customer ID, so the user can keep creating customers back-to-back without ever touching the ID field manually:

Dropdown_1.addItem(NewCustomerString, custName);
NewCustomerNumber = NewCustomerNumber + 1;
NewCustomerString = ConvertUtils.numberToString(NewCustomerNumber);
Text_9.applyText(NewCustomerString);

Note the dropdown on the Update form is also updated in the same step, so a customer created in this session is immediately available for editing without a page refresh.

OnSelect event of Dropdown- Selecting the customer to updates its properties:

When a user picks a customer from the dropdown on the Update form, the script reads that member's current properties and reflects them on screen — in this case, setting the Active/Inactive radio button to match the stored CustomerStatus:

var selectedId = Dropdown_1.getSelectedKey();

if (selectedId === "Select a Customer") {
    RadioButtonGroup_3.setSelectedKey("Active");
    return;
}

var customerDim = PlanningModel_1.getMember("Customer", selectedId);
RadioButtonGroup_3.setSelectedKey(customerDim.properties.CustomerStatus);

OnClick event of Button(Submit) - Updating properties of Customer:

On submit, only the changed attribute is sent back — there's no need to resend the description or other unrelated properties:

var custId = Dropdown_1.getSelectedKey();
var CustomerStatus = RadioButtonGroup_3.getSelectedKey();

if (custId === "Select a Customer") {
  Application.showMessage(ApplicationMessageType.Error, "Customer ID is required.");
  return;
}

allmember = ({
  id: custId,
  properties: { CustomerStatus: CustomerStatus }
});

PlanningModel_1.updateMembers("Customer", allmember);

This is a nice illustration of updateMembers behavior: you don't have to pass the full member object, only the ID plus the properties you actually want to change.

Conclusion

Planning cycles don't wait for master data to catch up, and this is a simple example of how a bit of scripting inside a Story can close that gap without any dependency on the source system. With just two forms and a handful of script variables, business users get a self-service way to onboard a new customer for planning purposes — with the ID handled automatically — and to keep that customer's status current as plans evolve.

1 Comment