Tuesday, May 29, 2012

Whenever Possible, USE APIs!

I surprised myself last week in writing a customer interface for AX 2009 in only a few hours.  Why is this surprising?  When AX 2009 first came out, most AX consultants (myself included) were a bit distressed by the Global Address Book (GAB) framework and the complexities it added to the customer and vendor import process.  Instead of importing data into one table, you have to hit 4 or 5 tables, some of which are just serving as a junction (N:N) table.  In case you're wondering, yes, it is even more complicated in AX 2012 with the introduction of table inheritance.

The reason I bring this up is to bring home a very important point:  whenever possible, USE APIs!  The only reason I was able to build an interface so quickly is because a simple static method call removed all of the complexity of the GAB framework.  As outlined in the Dynamics AX 2009 Global Address Book whitepaper (there's an equally simple approach for 2012), I used the following line of code to build all GAB data for me and assign a new Party ID to each customer:


custTable.PartyId = DirParty::createPartyFromCommon(custTable).PartyId;


APIs are your friends, and they exist to make your life a lot easier.  When you don't use APIs you are effectively:

  • Wasting brain power trying to understand a black box that doesn't necessarily need to be understood
  • Making more work for yourself both in building the solution and supporting it over its useful life
  • Creating more potential for error in terms of bugs and data consistency
  • Wasting client resources and/or project budget

So prior to diving in and building a solution, see if you can leverage existing APIs to address the problem you're trying to solve.  This principle applies not only to AX, but also to any mature language or enterprise software system.

Friday, May 18, 2012

Using Dynamic Ranges with Record Level Security

Disclaimer:  This article is more for my friends on 2009 and prior.  Record level security has been replaced with a more flexible framework in 2012 and will be obsolete in the next major release. For those who are not planning on upgrading to 2012 in the near future, this should make the old framework much more flexible.

In my last post I described the use of the SysQueryRangeUtil framework to filter forms and reports using dynamic values. In this post, I wanted to take this a step further and explain how using this framework in combination with record level security policies is a powerful combination.

As an example, say that a company wants to prevent purchasing users from viewing POs that do not belong to their site.

This is a fairly simple process:


Add a static method to the SysQueryRangeUtil class to calculate the user’s site

There’s a field on the Employee table (EmplTable) called Default Site (ReqSiteId) which is used for defaulting a site onto requisitions created by the user. This is also a good place to define the site that each employee belongs to. Now if we look at SysQueryRangeUtil.currentEmployeeId(), we find that the current employee ID is obtained through a single line of code.



By creating a new method called “currentEmployeeSite()” and slightly modifying the code used above, we can obtain the employee’s site.




Add a record level security policy to the purchasing security group


  1. Go to the Record Level Security form under Administration –> Setup –> Security and add a new policy
  2. Select the User Group the policy should apply to and click Next


  3. On the Tables page, select the Show All Tables radio button, expand Accounts Payable and check Purchase Orders
  4. Click Next and the Finish
  5. On the Record Level Security form, select the newly created record and click on the Query button
  6. Add a line to the Range grid as shown in the picture below


Test

Login as a user assigned to the user group with the new record level security policy. You should only see purchase orders tied to the Default Site in the user’s employee profile.



This same approach can be used directly an RLS policy for the Inventory Dimension table, which will filter data on ALL forms that join to the InventDim table.

Thursday, May 17, 2012

Filtering Data using Dynamic Ranges

DAX contains some powerful features for filtering data on forms and reports that are easily accessible to end-users, however it’s more powerful than most understand. There’s a common misunderstanding that data on forms and reports can only be filtered by static values, but there’s a little known framework that allows the use of Excel-like functions that calculate the filter value.

As an example, say that a company has a policy to check on open purchase orders over 60 days old at the end of every month. Without the use of a dynamic date range, users would have to manually run the Open Purchase Orders report every month and change the date range each time. A simpler and more automated approach would be to setup the report to be sent to the user by email every month and set the date range as “(greaterThanDate(-60))”. This function will dynamically calculate a date range of 60 days prior to the report execution and greater.

Even better, skip the report and apply the logic above to a query used by a List Page and/or Cue.

Here’s a comprehensive list of date functions:




This framework does not just address dates, it also can calculate the current user ID (currentUserId()) and employee ID (currentEmployeeId()). To see a full list of the functions available, reference the SysQueryRangeUtil class in the AOT.


If you have a need that isn’t addressed through the standard functions, it’s fairly easy to add new ones. They can also be used in conjunction with record level security to expand the limited possibilities that the framework allows. I will go into more detail on this in a future post.

New Blog!

I've been wanting to start a Dynamics AX oriented blog for a while, but just couldn't find the time.  My goal is to publish informative posts on DAX and the surrounding ecosystem, lessons learned, and useful features that have not been covered elsewhere.  

Hope you enjoy!

John