Saturday 28 June 2014

AX2012 R3 : X++ code to get WBS key for a project activity

Hi Friends,
I want to share a piece of code which can be used to get the value of WBS key of an activity for a project. This piece of code works well with AX2012 R3 and AX 2012 R2.
In projects module, we can define Work Breakdown Structure (WBS) for a project. A very nice integration with Microsoft Projects is already available out of the box in standard Microsoft Dynamics AX.
WBS key represents the hierarchy or level of that activity amongst all the project activities.The WBS for a project can be accessed from Projects list and details page from the "Work breakdown structure" button available under the "Plan" group of the action pane as shown below:


When you open this form, you will notice that unique WBS key is reflected against each activity. This value is dynamic, if you indend,outdent,move up, move down an activity, it will change the WBS key value. 
By dynamic value I mean it is not stored as a field in any table.So now lets explore the technical side of it.

 If you look at the form control, it is a display method coming from a method outlineNumber() written on HierarchyTreeTable.

Note this display method is not available directly on the table. If it would have been directly available on the table, then this would not have been an interesting topic to blog :). 
It is written as a display method on the form under the data-source methods as shown below:


So now we see that the display method returns the value from a function OutlineNumberFor() available on object of a class whose variable is declared with the name of controller on the form. So let's move ahead and look at the class declaration of the form, so we notice that this class is ProjWBSUpdateController



Now things get interesting, let us try to find on this form how this class is getting initialized, the best place to find this is init method of the form and you will notice the below code there:

wow...it requires so many parameters, let have a look at the new() method of this class, you will notice that only the first two parameters are mandatory and rest all have a default value assigned to them in the function definition and so are optional:



Additionally after initializing the class object the most important part is the call to updateOutlineNumbersAndPublishInPreOrder() method call. All the magic happens in this method and it makes your class object the shining star of the form ;).

So if we can get the hierarchyID and the calendarID, we can use this class to run all the business rules for us and return the key number. It is always a best practise not to reinvent the whole wheel and use the available classes out of the box.
HierarchyID --> If you try to analyse smmActivities table which stores all the project activities you will find that it is linked to HierarchyTreeTable based on it's recID and from HierarchyTreeTable you can get the hierarchyID.

CalendarID --> You can get it from the project.

So now time to knit all the above piece of code together and make is usable, so here is the job :


To quickly reuse the code here it goes:

static void wfsGetWBSForActivityNumber(Args _args)
{
    ProjWBSUpdateController         controller;
    HierarchyIdBase                         hierarchyId;
    HierarchyTreeTable                    hierarchyTreeTable;
    str                                               recordWBSId;
    smmActivities                              smmActivities;
    ProjTable                                    projTable ;
    CalendarId                                  calendarId;

    smmActivities = smmActivities::find("A000072");
    hierarchyTreeTable = hierarchyTreeTable::findRefRecId(smmActivities.RecId);
 
    calendarId = ProjTable::find(smmActivities.projId()).PSASchedCalendarId;
 
    hierarchyId = hierarchyTreeTable.HierarchyId;

    controller = new ProjWBSUpdateController(hierarchyId,calendarId);
    controller.updateOutlineNumbersAndPublishInPreOrder();
    recordWBSId = controller.outlineNumberFor(hierarchyTreeTable.ElementNumber);
    info(recordWBSId);
}

Below is the result when we run the above code for the below shown project:


Thanks for reading the blog. Have a nice day and keep sharing.