Dayalan Punniyamoorthy Blog

Saturday, May 31, 2025

Groovy Scripting in Oracle Cloud EPM: A Deep Dive!

 Groovy scripting in Oracle Cloud EPM Planning is not just a scripting tool—it's a superpower. With the ability to manipulate forms, validate data, execute calculations, and orchestrate workflows dynamically, Groovy turns your Planning environment into a living, breathing application.

Whether you’re validating input, enforcing business rules, or orchestrating complex data flows, Groovy scripting provides flexibility and control that traditional Planning business rules can only dream of.


Understanding the Multidimensional Mindset

Before jumping into the code, it's critical to understand the Essbase multidimensional model behind the curtain. In EPM:

  • A cube (also called a database) holds data structured by dimensions like Account, Entity, Period, Year, Scenario, and Version.

  • Each dimension is a tree of members. Data is stored at leaf members (Level 0), and upper-level members aggregate this data.

  • Shared members, alias tables, sparse vs. dense configurations, and alternate hierarchies all play a role in optimizing performance and usability.

Knowing your data structure is as essential as knowing your script.


Groovy API Basics

Your entry points in every script are the EpmScript methods and the operation object. Here are the most essential patterns:


Accessing the Grid

Use this to validate or transform form data:

/* RTPS: */

if (!operation.hasGrid()) {

    throwVetoException("No grid available to process!")

}


Grid grid = operation.grid

grid.dataCellIterator().each { DataCell cell ->

    cell.bgColor = 0xFFFF00  // Highlight all cells yellow (because why not?)

}


Retrieving Data from the Cube

When you need data that's not on the form, use a FlexibleDataGridDefinitionBuilder.

/* RTPS: */

Cube cube = operation.application.getCube("FinPln")


cube.flexibleDataGridDefinitionBuilder()

    .with {

        setPov(['Actual', 'Final', 'Entity1', 'FY25'])

        addColumn(['Jan', 'Feb', 'Mar'])

        addRow(['Salary'])

        build()

    }

    .with { gridDef ->

        cube.loadGrid(gridDef, false).withCloseable { grid ->

            grid.dataCellIterator().each { DataCell cell ->

                println("${cell.getMemberNames()} = ${cell.data}")

            }

        }

    }


Writing Data

Use DataGridBuilder to save values programmatically:


/* RTPS: */

Cube cube = operation.application.getCube("FinPln")

DataGridBuilder builder = cube.dataGridBuilder("05/31/2025")


builder.addPov("Plan", "Working", "Entity1", "FY25")

builder.addColumn("Jan", "Feb", "Mar")

builder.addRow(["Salary"], [10000, 12000, 13000])


DataGridBuilder.Status status = new DataGridBuilder.Status()

builder.build(status).withCloseable { grid ->

    cube.saveGrid(grid)

    println "Accepted: ${status.numAcceptedCells}, Rejected: ${status.numRejectedCells}"

}


Validations

Groovy is also a superhero for data validation. Here's how you veto a cell based on a rule:

/* RTPS: */

if (!operation.hasGrid()) {

    throwVetoException("Expected form data not found.")

}


operation.grid.dataCellIterator().findAll { it.account == "Salary" }.each { DataCell cell ->

    if (cell.data > 50000) {

        cell.addValidationError(0xFFFF00, "Salary exceeds allowed maximum.")

    }

}


  • Always check operation.hasGrid() before calling operation.grid.
  • Use withCloseable blocks for all cube grid operations.

  • Prefer findAll() to filter collections before iteration.

  • Always explicitly declare types (Cube, Grid, etc.).

  • Never return anything from a script unless it’s a string of valid Essbase calc commands.


Hope this was useful!!!

No comments:

Post a Comment