Skip to content

Utility.Copy Scenario.Simple

The below code is used to copy data from a Scenario to a Scenario within a Cube.

TIP

By changing cube.slice to cube.sliceStatic, formula values will be ignored, and only cells with a static value will be sliced.

Process Code

vb
/**
 * MODLR PROCESS SCRIPT: Copy Cube Data Between Scenarios
 * -------------------------------------------------------
 * Purpose:
 *   Copies every value in a cube from one Scenario element ("from") to
 *   another Scenario element ("to"), leaving every other dimension
 *   untouched. Optionally wipes the destination scenario slice first so
 *   the copy is a clean replace rather than a merge on top of old data.
 *
 */

var scenarioDimensionName = "Scenario"; // name of the dimension holding scenario elements in the target cube

/**
 * pre()
 * Runs once before the process executes. Registers the prompts MODLR
 * asks for at run time.
 */
function pre() {
    script.log('process pre-execution parameters parsed.');

    // Answers become the globals cube_name / scenario_from / scenario_to /
    // should_wipe, available in begin()/data()/end().
    script.prompt("Cube Name", "cube_name", "");
    script.prompt("Scenario From", "scenario_from", "");
    script.prompt("Scenario To", "scenario_to", "");
    script.prompt("Wipe Destination", "should_wipe", "1"); // "1" = wipe destination scenario first, anything else = don't
};

/**
 * begin()
 * Runs once at the start of the process:
 *   1. Validate the cube, the Scenario dimension, and both scenario
 *      elements exist.
 *   2. Optionally wipe the destination scenario slice.
 *   3. Slice the cube at the source scenario and copy every value
 *      across to the same coordinates under the destination scenario.
 */
function begin() {
    script.log('process execution started.');

    // No source scenario supplied — nothing to copy, exit quietly.
    if (scenario_from == "") {
        return
    }

    // Abort if the cube doesn't exist.
    if (!cube.exists(cube_name)) {
        script.abort("The specified cube is not found: " + cube_name);
        return;
    }

    // Find the Scenario dimension's position in the cube's dimension list
    // (its index is used later to know which slot in each element array
    // corresponds to the scenario).
    var scenarioPosition = -1;
    var dims = JSON.parse(cube.dimensions(cube_name));
    for (var i = 0; i < dims.length; i++) {
        var dim = dims[i];
        if (dimension.getName(dim.id) == scenarioDimensionName) {
            scenarioPosition = i;
            break;
        }
    }

    // Abort if the cube has no Scenario dimension.
    if (scenarioPosition == -1) {
        script.abort("Could not find the Scenario dimension within the target cube.");
        return;
    }

    // Abort if the source scenario element doesn't exist in that dimension.
    var scenarioDim = dims[scenarioPosition];
    if (!element.exists(scenarioDim.name, scenario_from)) {
        script.abort("Could not find the Scenario from.");
        return
    }

    // Abort if the destination scenario element doesn't exist in that dimension.
    var scenarioDim = dims[scenarioPosition];
    if (element.exists(scenarioDim.name, scenario_to) == false) {
        console.log("Could not find the Scenario: " + scenario_from + " within the scenario dimension: " + scenarioDim.name + `. Adding this ${scenarioDimensionName} to the Default hierarchy`);
        hierarchy.group(scenarioDimensionName, "Default", "", scenario_to)

    }


    // the whole destination-scenario slice is generated.
    var elements = [];
    for (var i = 0; i < dims.length; i++) {
        if (i == scenarioPosition) {
            elements.push(scenario_to);	// wipe the destination scenario slice
        } else {
            elements.push("");
        }
    }

    // Optionally wipe the destination scenario slice before copying, so
    if (should_wipe == "1") {
        cube.wipe(cube_name, elements);
    }

    // Reuse the same elements array as the slice filter, now pointed at
    // the source scenario, with every other dimension unfiltered ("") so
    // cube.slice() returns every cell under scenario_from.
    elements[scenarioPosition] = scenario_from;

    // Turn off cube logging for the duration of the copy — logging every
    // individual cube.set() call below would be noisy and unnecessary.
    cube.log(cube_name, false);

    // Slice the cube at the source scenario (every other dimension = all members).
    console.log("Slicing", cube_name, elements)
    var slice = cube.slice(cube_name, elements);
    for (var elms of slice) {
        // Build the destination coordinate set: identical to the source
        // element combination, except the scenario slot is swapped for
        // scenario_to.
        var elmsSet = [];
        for (var i = 0; i < elms.length - 1; i++) {
            if (i == scenarioPosition) {	// swap in the destination scenario
                elmsSet.push(scenario_to);
            } else {
                elmsSet.push(elms[i]);		// same element as the source slice
            }
        }

        // The last item in each slice row is the cell's value.
        var value = elms[elms.length - 1];
        if (value) { // skip empty/null cells - nothing to copy
            // Write the source value into the destination scenario coordinates.
            cube.set(value, cube_name, elmsSet);
        }
    }

    // Turn cube logging back on now the bulk copy is done.
    cube.log(cube_name, true);
}

/**
 * data(record)
 * Runs once per record on a second pass. Not used by this script —
 * the copy is done entirely in begin() in a single pass.
 */
function data(record) {

}

/**
 * end()
 * Runs once at the end of the process.
 */
function end() {
    script.log('process execution finished.');
}