Thursday, March 1, 2018

Metadata tricks using Groovy in EPBCS


I have been very busy the past couple of months. New job, new projects, frequent travelling, and a lot of running in preparation for my next race in TWO weeks in Katoomba in the Blue Mountains. I'm running Six Foot Track, Australia's most famous trail run and one of the toughest, a 45km trail run with 1600 meter of elevation change. Wish me luck!

This post is about using Groovy in metadata management in EPBCS. In my example I will be showing a very simple model built for a Project Planning application that mainly involves:

  1. Approving New Projects
  2. Automatically assigning Project Codes to the approved projects based on the existing sequence
  3. Assign and move the Approved Project to another hierarchy hierarchy (Construction, IT, Business, Service..)

Let me start by showing my Project dimension outline.




So I have four Porject categories:

  1. Construction
  2. IT
  3. Business
  4. Services

Approved Projects are prefixed with P_ and have four digits number. Moving on to "Project Approvals" card where I will be handling the New and Approved Projects.





Vertically tabbed card showing New and Approved projects .The form shows all of the New Project requests that needs to be approved or rejected sitting in the first tab. At this stage I'm simply capturing the description and the total Budget for the sake of simplicity. Notice the information is captured at "No Entity" intersection.




In the second tab I have the Approved Projects with more information like Status, Approver, Comments. I'm also intersecting the Approved Projects with the relevant Entity (Will come back to this when I talk about the business rule).






So now I want to approve "New_Project_006" and see what happens. I have attached my main business rule to a Menu and I'll simply right-click on the Project and launch the "Approve" rule.



I have three rtps to enter:

  1.  Comments (with simple validation to make sure there are at least n number of characters entered)
  2. Parent Project to move the newly approved project under
  3. Department (Entity) to select the relevant department for the approved project


I'm restricting the Parent rtp to the children of "Total Approved" as shown below.





I'm also selecting a department from a hierarchy-driven SmartList.







If you want to learn more about hierarchy driven SmartList you must read Celvin's posts, and Pete's post.


As you can see in the screenshot above, I have provided a comment, selected "Construction" as a parent for the approved project, Finance as my department, and will launch the business rule.






My new project was approved and is no longer shown in the New Projects tab. I'll go to Approved Projects to see what happened. There is a new member "P_1005" intersected with Finance entity with project information and approval details. 





The Approved Projects outline now looks like:





Illustration of what just happened:




Nice huh? You betcha!!


Back to my New Projects form, I'll try and approve another project and see if the rtp comments validation works.







"Yeha approved" is my comment for approval and it's not going to cut it as shown below!.







Sorry, never again Groovy! I'm going to behave from now on.

Now my project is approved (after putting proper comments) and assigning it to IT category and Finance department.





My Approved Projects (notice the new project P_1006).






My Project dimension outline.






And that's it really. Powerful stuff if you ask me, the sky is the limit with Groovy (quite literally).

Main business rule.






 1 
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*RTPS: {Project} {Year} {Scenario} {Version} {Approver} {Comments} {Department} {ProjectParent}*/
def mbUs = messageBundle(["validation.invalidComment":"You must have at least 15 characters in comments rtp. {0}"])
def mbl = messageBundleLoader(["en" : mbUs]);
validateRtp(rtps.Comments, /^.{15,}$/, mbl, "validation.invalidComment", rtps.Comments);
Dimension project = operation.application.getDimension("Project")
def existingProjects = project.getEvaluatedMembers("ILvl0Descendants(Total Approved)", operation.cube )
existingProjects.sort()
def lastApprovedProject = existingProjects
String lastApprovedProjectCode = lastApprovedProject.last()
int i = lastApprovedProjectCode.find(/\d+/).toInteger()
i++
String newApprovedProject = "P_"+i
Member parentProject = rtps.ProjectParent.getMember()
Map approvedProject = parentProject.newChildAsMap(newApprovedProject)
// Save the new approved ptoject
Member approvedProjectMember = project.saveMember(approvedProject, DynamicChildStrategy.ALWAYS_DYNAMIC)
println rtps.Project.toString() + " is approved. New approved code is " + newApprovedProject
// Copy data to the new project member
String script = """Set CreatenonMissingBlk On;
Fix(${fixValues(rtps.Scenario, rtps.Year, rtps.Version)})
Fix(${fixValues(approvedProjectMember)},@Member(@Concatenate("HSP_ID_",@HspNumToString({Department}))))
"BegBalance"
(
"Project Description" = "Project Description"->"No Entity"->$rtps.Project;
"Project Status" = 1;
"Approved By" = $rtps.Approver;
"Project Total Budget" = "Project Total Budget"->"No Entity"->$rtps.Project;
"Project Approval Date" = @CalcMgrGetCurrentDate();
"Comments" = $rtps.Comments;
)
EndFix
Fix(${fixValues(rtps.Project)},"No Entity")
ClearBlock All;
EndFix
EndFix
"""
println script
return script.toString()



I'll explain the important bits in the rule (read my earlier posts for more explanation).

Validating the comments rtp.



validateRtp(rtps.Comments, /^.{15,}$/, mbl, "validation.invalidComment", rtps.Comments);




Working out the new approved project code.



Dimension project = operation.application.getDimension("Project")
def existingProjects = project.getEvaluatedMembers("ILvl0Descendants(Total Approved)", operation.cube )
existingProjects.sort()
def lastApprovedProject = existingProjects
String lastApprovedProjectCode = lastApprovedProject.last()
int i = lastApprovedProjectCode.find(/\d+/).toInteger()
i++
String newApprovedProject = "P_"+i



Copy the data to the new approved project member, capture the Project Description, Status, Approver (using StringAsNumber Cal Manager variable), Total Budget, Approval date, and the Comments (using StringAsNumber Calc Manager variable).


String script = """Set CreatenonMissingBlk On;
Fix(${fixValues(rtps.Scenario, rtps.Year, rtps.Version)})
Fix(${fixValues(approvedProjectMember)},@Member(@Concatenate("HSP_ID_",@HspNumToString({Department}))))
"BegBalance"
(
"Project Description" = "Project Description"->"No Entity"->$rtps.Project;
"Project Status" = 1;
"Approved By" = $rtps.Approver;
"Project Total Budget" = "Project Total Budget"->"No Entity"->$rtps.Project;
"Project Approval Date" = @CalcMgrGetCurrentDate();
"Comments" = $rtps.Comments;
)
EndFix
Fix(${fixValues(rtps.Project)},"No Entity")
ClearBlock All;
EndFix
EndFix
"""
println script
return script.toString()


The script above is really simple, only thing worthy of mention is me using the Entity dimension hierarchy driven SmartList to move the data from "No Entity" to the user selected entity. Again you can read Pete's detailed post to understand more.



This is the business rule log.







So to recap, Groovy is pretty awesome! it offers a lot of different and powerful design options. My example is pretty straight-forward but you can easily build more complex scenarios and take it from there.



Wish Me Luck in Six Foot track๐Ÿ’ช๐Ÿ™๐Ÿ˜Ž