Almost every ePBCS application will require the need to run business logic and execute data movements on specific months based on a scenario selected.  Almost every Data Form will require action to be taken dynamically based on the selection of the Scenario.  Assuming a fiscal year of Jan to Dec and Actuals being final through Jun,

  • processes on Actuals will run on Jan through Jun
  • processes on Budget/Plan will run on Jan through Dec
  • processes on Forecast will run on Jul through Dec

Prior to Groovy, this was done in an Essbase calculation by writing if statements based on the scenario selected in the POV and checking if the month was part of a variable range.  It might have looked something like this.

IF((@ISMBR("Plan") AND @ISMBR(&v_BudYear) AND @ISMBR(&v_BudMths)) OR
  (@ISMBR("Forecast") AND @ISMBR(&v_FcstYr1) AND @ISMBR(&v_FcstMths1)))
 ...
ENDIF

Groovy provides a much more efficient way to accomplish this.  Although it may be different based on specific needs, the example below will assume that Forecast and Actuals can be identified based on a variable that is updated monthly identifying the last month of Actuals.

Setting Up The Variables and Run Time Prompts

This application has a substitution variable named v_CurMonth used to identify the current reporting month of Actuals, or the last month Actuals are final.  This will be used in the Run Time Prompt as a default value in several places and this is required.

A Run Time Prompt is also required.  There is nothing unique about the RTP for this use and the application may likely already have one.  This will be a member Type, be connected to the Period dimension, and have a default value of the substitution variable above.  It would look similar to this.

Finally, a variable in the Groovy business rule must be created.  This variable will be set as an override value with the default value equal to the substitution variable created above.  In a Groovy business rule, a variable is instantiated by adding the following line at the top of the rule.  It looks like a comment, but it acts differently when it starts with RTPS:.

/*RTPS: {RTP_Month}*/

After this is added, the Variables tab of the business rule will show all the variable defined.  Set the variable so that it is hidden and is used as an override value.

The Code

At this point, all of the required variables are setup.  The first step is to define the months.  This can be hard coded since this is not something that will change frequently.  If this is added to a script and included in all the business rules it is needed, changing it would be easy if the months did change since it is in one place and shared throughout.

// Hard coded months 
def months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']

Optionally, this can be set dynamically.  Either option, in my opinion, is acceptable.  The result of the following would produce the exact same value for months.

// Dynamically get the list of months based on the application's fiscal year 
Cube cube = operation.application.getCube("GP") 
Dimension productDim = operation.application.getDimension("Period", cube) 
def months = productDim.getEvaluatedMembers("ILvl0Descendants(YearTotal)", cube)

The next piece of this rule is to get the month from the variable.  This will be the key to identify which months are used in calculations that run on Actuals and Forecast.

// Get the month from the hidden RTP 
def actMonth = rtps.RTP_Month.toString()

The last step is to get the appropriate months based on the Scenario selected.  If Actuals is selected, all the elements in the months list from the first to the index of the month in the RTP are selected.  If Plan is selected, all the months are included.  This can be altered based on needs.  Some companies do mid year plans, for example, and may need to only execute the logic on the last 6 months.  If Forecast is selected, all the elements in the months list AFTER the RTP value to the end of the list are selected.

// Create dynamic list of months based on user selected Scenario
def useMonths = []
if(operation.grid.pov.find{it.dimName =='Scenario'}.essbaseMbrName.toString().toLowerCase().contains('plan')){
  // This will create a list of all months - assuming the the plan is for 12 months
  useMonths = months 
  }
if(operation.grid.pov.find{it.dimName =='Scenario'}.essbaseMbrName.toString().toLowerCase().contains('forecast')){
  // This will create a list all months after the actMonth variable - or the out months
  useMonths = months[months.findIndexOf{it == actMonth}+1..-1] 
  }
else{
  // This will create a list of all actual months
  useMonths = months[0..months.findIndexOf{it == actMonth}] 
  }

// Create delimited list for methods that don't require a List object
def useMonthsString = """\"${useMonths.join('", "')}\""""

Using useMonths and useMonthsString

The hard part is over.  Using the variable is the easy part.  It can be used anywhere the months needs to be change from all months to the identified months.

In a DataMap/SmartPush

operation.grid.getSmartPush("GP_SmartPush").execute(["Period":useMonthsString])

Same concept but overriding the entire POV (something more realistic in a real world example)

// This creates a variable for the povMap that includes all members in the existing POV
// This is used as a possible scenario but is not directly related to the content of this post
def povMap = operation.grid.pov.each{povMbrs["pov$it.dimName"] = "$it.essbaseMbrName"}
// Add the months to a map for the Period dimension
povMap['Period'] = useMonthsString
operation.grid.getSmartPush("GP_SmartPush").execute(povMap)

In a GridBuilder

builder.addColumn(['Period','Currency'], [ useMonths,['Local','USD'] ])

In an Essbase FIX statement

essCalc << """
FIX($useMonthsString)
"""

Finally

This could be added to a script and included in all the Business Rules that require such a variable.  It creates a variable once that can be used in many classes.  It improve maintainability and reduces replicating the same logic for each class.  If you have any suggestions, or have something you would like to share, please post a comment.

 

 
4 replies
  1. Aamir says:

    Hi,
    Thanks for sharing detail information on Dynamic Range creation with Groovy.
    I have one question on as you mentioned above, Can we use Substitution variable as value for RTP?

     
    Reply
    • Kyle Goodfriend says:

      Glad it is helpful, and yes you can. If you make your RTP in your rule an override by clicking the checkbox, then adding your subvar as the value, the subvar will be in your RTP. Earlier this year Oracle added a substitution variable class, so there is a method on the application and the cube class to get your subvar. This is my preferred way to get it now that it is available to grab outside of using REST. Depending on your scope, these are the two ways to grab the subvar. There is nothing for a subvar at the rule level/scope.

      operation.application.getSubstitutionVariable(java.lang.String name)
      operation.application.cube.getSubstitutionVariable(java.lang.String name)

       
      Reply
  2. SA says:

    Hi Kyle,

    I’m getting an ERROR – “A method called by the script failed on line:.., with error: The property [grid] is not valid for the current operation.” while running the business rules – “if(operation.grid.pov.find{it.dimName ==’Scenario’}.essbaseMbrName.toString().toLowerCase().contains(‘forecast’))” although the business rule validates successfully…..
    Really appreciate the help in debugging the error.
    Thanks

     
    Reply
    • Kyle Goodfriend says:

      This is typically seen if you reference the grid object but you aren’t running the rule on a form. If you try to run it from rules or from calc manager, there is no grid to reference.

       
      Reply

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.