Adventures in Groovy – Part 42: Using Scenario Start and End Periods Through Metadata Queries Provides Awesome Functionality

, , , ,

Identifying the months to calculate plan and forecast has always been a delicate balance between performance and maintenance.  Having a calculation for each required duplication of forms or calculating more than what was required.  Calculations required more complicated if logic than what it should have or (I hate when I see this) a calculation that just does both forecast and plan regardless of what needs calculated.

Groovy has opened up a lot of options.  We can identify if plan or forecast is in the form and dynamically build the Essbase calculation, which is what I have started doing.  This, combined with only calculating on the edited cells, gets you part of the way there.  It doesn’t help with global calculations or calculations when a single month will impact all months, like when there are balance walk forwards.

Groovy provides us the ability to get member properties, which I have discussed before.  The Scenario dimension has unique properties, like the start month, start year, end month, and end year, which is what creates the security on the periods and years for each scenario.  Once these are identified, the possibilities are endless.  They can be used to create gridbuilders, they can be used to dynamically fix on the correct periods in Essbase calculations, or they can be used to do other date specific things.

Groovy Calculation and API

At this point, you are probably very well aware of the operation class, which has an application sub class.  The application subclass has many functions within it that are useful.  One, is the ability to create a dimension object.  From there, a member object can be created.  The following is a very simple example to hold a scenario member in a member object that can be used in many ways.

Member mbrScenario = operation.application.getDimension('Scenario').getMember('Plan')

If the scenario is a run time prompt, it would look like this.  Assume the RTP is named rtpScenario.

Member mbrScenario = rtps.rtpScenario.getMember()

When the member object is initiated, all the properties of the specific member are available.  You may want to review Adventures In Groovy – Part 11: Accessing Metadata Properties before continuing.  The same concept is used here, but the properties returned are slightly different.  The four we want to access are

  • Start Period
  • End Period
  • Start Year
  • End Year

These can be stored in variables or used within calculations or other logic.  There may be a need to convert the month member to a long month or a number.  The year may also be converted to remove the FY and prepend 20 so it can be used in date manipulation.  println mbrScenario.toMap() produces the following, so the full list of properties is a little different than what was produced in Part 11.

Log messages : 
{Parent=Scenario, Two Pass Calculation=false, Process Management Enabled=true, Old Name=Plan, Formula=<none>, End Year=FY20, UDA=, Aggregation (GTech)=+, Solve Order (GTech)=0, Essbase Name=Plan, UUID=91f18d30-fc37-4e0b-bc9a-99668194e793, Member=Plan, Data Storage=never share, Hierarchy Type=none, Allow Upper Level Entity Input=false, End Period=Dec, Start Period=Jul, Aggregation (ProdRev)=+, Include BegBal=true, Data Storage (GTech)=never share, Formula Description=<none>, Data Id=2573227211374885, Data Storage (ProdRev)=never share, Start Year=FY18, Data Type=unspecified, Formula (GTech)=<none>, Old Unique Name=<none>, Formula (ProdRev)=<none>}

Since this is a Groovy map, a type of Groovy collection, any of the elements can be accessed by referencing the element key.

println mbrScenario.toMap()["Start Year"].toString()

Multi Year Example

Since many applications will span over multiple years, there is some additional logic that has to be built to loop through the months.  If the user is prompted for a start period/year and end period/year, you may need to validate that the date range is within the start/stop periods on the scenario, which also is easier to evaluate if these are converted to dates.

[membership level=”0″]


In2Hyperion has premium content that is only available to those who make a small annual donation.

 Already have an account with
in2Hyperion?

Login

 Want to purchase a subscription to
the premium content?

Purchase Today


[/membership]
[membership]

Building The Date Range

Creating a map with the year and the list of months is a great way to store the date range.  It can be used to build lists for gridbuilders that read data (parameters are multiple rows/columns by year with periods as a list object), creating Essbase calculations by year, and headers for grids that are submitting data (which are string arrays).

Expanding on what was previously discussed, this will convert the year to a valid year by removing FY and create a date object that stores the start and end dates in variables.  Notice the start date uses a day of 1 and the end date uses a day of 2.  This is important later on.

Member mbrScenario = operation.application.getDimension('Scenario').getMember('Plan')
Date scenarioStart = Date.parse("yyyy-MMM-dd", "20${mbrScenario.toMap()["Start Year"].toString().substring(2)}-${mbrScenario.toMap()["Start Period"].toString()}-01")
Date scenarioEnd = Date.parse("yyyy-MMM-dd", "20${mbrScenario.toMap()["End Year"].toString().substring(2)}-${mbrScenario.toMap()["End Period"].toString()}-02")

Now that we have two dates, the next step is to create the map to hold the results.  This map will look like similar to this for a date range when Jul,19 through Dec, 20 is selected.

['FY19',['Jul','Aug','Sep','Oct','Nov','Dec'] , ['FY20',['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']

This is created by looping through the two dates by month.  This example uses a while loop and will iterate through each month WHILE the iteration date (current) is before the end date.  If the scenarioStart and scenarioEnd dates had the say day, this would exclude the last month because they would be the same.  This also uses the calendar class to increase the date by month, which isn’t available in the date class.

// Create the variables needed
// PeriodRangeMap holds the years/months
// calendar and current are used in the iteration process
Map<String, List<String>> periodRangeMap = [:]
Calendar calendar = Calendar.getInstance()
Date current = scenarioStart

while (current.before(scenarioEnd)) {
  // Set the calendar variable to the date of the current variable 
  calendar.setTime(current)
  // If this is a new month, create a null map element to hold the new year and months
  if(periodRangeMap["FY${current.format("YY")}".toString()] == null){
    periodRangeMap["FY${current.format("YY")}".toString()] = []
  }
  // Append the month to the appropriate year
  periodRangeMap["FY${current.format("YY")}".toString()] << current.format("MMM").toString()

  // Increase the calendar variable by one month
  calendar.add(Calendar.MONTH, 1);
  // set the current variable to the next month's date
  current = calendar.getTime();
}

If we print the periodRangeMap to the job console using a println, assuming the start month is Jul of 18 and the end month is Dec of 20, this will be displayed with the exception that line returns are added to make it readable.

Log messages : 
{
FY18=[Jul, Aug, Sep, Oct, Nov, Dec], 
FY19=[Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec], 
FY20=[Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
}

The periodRangeMap can then be used to loop through and create the lists and maps used in gridbuilders and datamaps.

[/membership]

Last Call

The ability to directly access the start and stop period/year really gives partners a new thought process to implementation options where these periods are critical.  It is very easy to access, very easy to implement, and extremely useful.  If you have an idea of how to use these dates, share them with the community so we can all benefit.  Hope to see an idea soon!

 
4 replies
  1. Mahesh says:

    Hi Kyle,

    setForceEditable(true), i enabled this option to users to be able to write to essbase. but this setting doesnt really save the data if its outside of scenario start period and end period. just trying to understand what is the use of this force edit if we are not able to save the data. could you please clarify.

    thanks
    Mahesh

     
    Reply
    • Kyle Goodfriend says:

      You are correct. You can use it for all kinds of things though. You can allow input so users can enter data. You can use it in before save events to calculate data. You can use it in a dynamic Essbase calc, which overrides security all together. I use it a lot to allow input into parents, or into dynamically calculated members. If you have a dyn calc rate, say average cost, and you have units and total cost, you can allow a user to enter either units, the average cost, or the total cost, even allow them to lock a one of the other two, and then calculate the third. So there are a ton of options here.

       
      Reply
  2. jonathan says:

    Hi Kyle,

    Can you override the “open” periods on a scenario with a DataGridBuilder save function? (i.e. I want to save data and calculate technically closed periods in a forecast for headcount planning in order to calculate a full year salary and and handle tax maximums correctly in the open forecast years). I tried execute as SYSTEM_USER, but that didn’t work.

    Best Regards,
    Jonathan

     
    Reply
    • Kyle Goodfriend says:

      Not really. You could change the open period in a rule, then submit your grid builder, then change it back. This would work for administrative processes but you wouldn’t want to be flipping this when users are in the application. What I have done is have another scenario that has the needed periods open, then I do a copy with an essbase calc to move it from the open period to the closed period.

       
      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.