Model Management: Tools – Eventing

Since AX 2012 brought in the concept of models I have been very interested in utilising them to their full potential with our clients and our products. With certain things such as entirely new tables, classes, forms etc, the model concept works very well. It even works well when it comes to individual components on forms or new fields or methods as they are all regarded as individual objects in AX that can be plugged in and out of AX with models.

Where things start to become a bit harder is on shared objects such as standard methods etc. So how do we overcome some of these challenges. Hopefully over the next while I will post some useful design patterns and tools that one can use to keep one’s models clean and separated where at all possible. An added bonus of keeping your code separate is that it make performing Cumulative updates or 3rd party updates alot easier as there is less overshadowing.

Eventing

One of the most useful tools for keeping models separate is the new Eventing functionality in Dynamics AX 2012. Eventing has a number of benefits, but in regards to model management eventing allows one to keep your code (the event handler method) in a separate model to calling class as well as keeping the event subscription that calls your code in your separate model.

Eventing on Table Methods e.g. initFrom

Using event handlers and subscriptions on table methods are one of the areas that are very useful for model management. For example you may have added new fields to a standard table such as PurchTable that you would like to initialise as part of the “initFromRFQTable” method without overshadowing standard AX code. Using event an event handler you could do the following;

1. Create New class e.g. MyEventHandler
2. Add a new static method to the class e.g. initFromRFQTable that accepts an XppPrePostArgs object as a parameter.
3. Get a reference to the calling object, in this case the PurchTable e.g. PurchTable purch = ppArgs.getThis();
4. Get a reference to the calling method’s parameter e.g. _purchRFQTable
5. Perform any initialisation that is necessary e.g. purch.MyField = rfq.MyField;

public static void initFromRFQTable(XppPrePostArgs ppArgs)
{
   PurchTable purch = ppArgs.getThis();
   PurchRFQTable rfq = ppArgs.getArg('_purchRFQTable');
   purch.MyField = rfq.MyField;
}

Note that this method has access to all the parameters of the PurchTable::initFromRFQTable as well as access to the PurchTable object itself, so you can perform close to anything that you would be able to perform in the method itself.

For a direct example look at how Microsoft has done this for localisations on the PurchTable’s initFromVendTableMethod.

In the next few weeks I’ll publish more model management tools, tricks and design patterns. If you have any of your own please let me know as well.

 

Active Directory Lookup in AX 2012

Requirement:
As you may be aware by now, AX allows one to create users of type “active directory group” which if setup will auto-create users who belong to that group when they try to login. Furthermore users (whether auto-created or manually created) who belong to these groups will inherit the security permissions assigned to these groups.

One challenge however is, to debug this. I.E. Finding out which users are members of specific groups or what groups a specific user belongs to. My previous post was about how to determine ownership via command line. After a bit of reflection I thought this may be better and more useful to have this functionality directly within AX. Using Attached is an XPO with the relevant code (use at your own risk).

Screen Shot 2014-09-08 at 12.37.46 PM

Download

Basically it adds a class to AX as well as a lookup menu item to UserListPage form and the User form.

Please let me know your comments.

Here is a sample job if you don’t want to download the full project. It prints all groups for a user.

static void adGroups(Args _args)
{
    System.DirectoryServices.AccountManagement.PrincipalContext yourDomain;
    System.DirectoryServices.AccountManagement.UserPrincipal user;
    System.DirectoryServices.AccountManagement.GroupPrincipal p;
    CLRObject groups, enum;
    System.String domain, username1,groupName;
    str groupN;
    Userid userId = curUserId();
    InteropPermission permission;

    UserInfo userInfo;
    try
    {
        permission = new InteropPermission(InteropKind::CLRInterop);
        permission.assert();

        yourDomain = new System.DirectoryServices.AccountManagement.PrincipalContext(System.DirectoryServices.AccountManagement.ContextType::Domain);

        // find your user
        userInfo = xUserInfo::find(false, userId);
        domain = UserInfo.networkDomain;
        username1 = UserInfo.networkAlias;
        user = System.DirectoryServices.AccountManagement.UserPrincipal::FindByIdentity(yourDomain, username1);

        // if found - grab its groups

        if(user != null)
        {
            groups = user.GetAuthorizationGroups();
            enum = groups.GetEnumerator();

            while (enum.MoveNext())
            {
                p = enum.get_Current();
                groupName = p.get_Name();
                groupN = groupName;
                info(groupN);
            }

        }
        CodeAccessPermission::revertAssert();
     }
     catch (Exception::CLRError)
     {
         CodeAccessPermission::revertAssert();
         info(CLRInterop::getLastException().ToString());
     }
}

 

Domain Users / Group Checking

As you may all be aware, AX 2012 offers certain ability to manage your Dynamics AX 2012 users via Active directory groups. Hopefully more on that later. (Check out this post in the meantime http://blogs.msdn.com/b/dynamics-coe/archive/2013/01/13/using-windows-ad-groups-for-user-management-in-ax2012.aspx)

The one problem however is that it becomes quite difficult to trouble shoot security settings. For example how to determine whether a user is actually part of an active directory user group. If you are not a domain admin you can try and use the following two command prompt instructions

1. To detemine an individual user’s groups
net user /domain [username]
e.g.: net user /domain jonathanhalland

2. To detemine all users belonging to a specific group
net group /domain [groupname]
e.g.: net group /domain axrequisitioncreators

The client cannot download the services metadata from…

Problem / Symptoms: You receive the following message whenever running code or services running in CIL:  The client cannot download the services metadata from http://[SERVERNAME]:8101/DynamicsAx/Services/AxClient?wsdl. The metadata address of the AOS services is incorrect or the AOS services are down. Please contact your system administrator.

Description: When running code in CIL (e.g. Services, batch jobs, Reports etc) you may receive the above error message. The cause of this problem could be multiple, but it basically means that the AOS or client cannot connect to the AX2012 services for one reason or another.

Screen Shot 2014-08-13 at 3.59.55 PM

 

Resolution: This could possibly be a DNS or some networking failure that you can test by trying to ping the the server that the services run on and resolving the issue as needed.

In our specific case, we experienced an issue that is probably very rare, we have a proxy server configured on the AOS server’s Internet options. However the “Bypass proxy server for local addresses” was not selected, therefore the server could not even access services located on itself. To resolve:

  1. Open Internet Explorer on the AOS.
  2. Click on the settings icon
  3. Click Internet Options
  4. Select Connections
  5. Click “Lan Settings”
  6. Ensure the “Bypass proxy server for local addresses” is checked
  7. Close all screens
  8. Restart AX client and run your code again.

Screen Shot 2014-08-14 at 11.05.52 AM

Back porting AX2012 XPO to AX 2009

Problem / Symptom: AX2012 XPO files are not generally compatible with AX 2009. Description: When porting an XPO created in AX2012 back into AX2009 one is unable to do a compare against various types of object such as forms, classes, tables etc…

Solution: NOTE 1: This solution is to aid you to back port basic code and tables but shouldn’t be regarded as a production ready solution. There is certain functionality that simply cannot be back ported such as event subscriptions etc…

NOTE 2: This is a solution in progress, please let me know if you find any additional changes that one needs to make in order to back port various other AOT XPOs.

Its not often that one needs to port code from a newer system into an older system, but sometimes the occasion does arise as it did for me this week. We have some new functionality in our AX2012 products that we really wanted to port back to AX2009 and a manual compare with both systems open is not really an option and doesn’t actually work for most objects.

The critical areas for us to be able to port back are the tables, classes and forms, all three don’t work. When selecting an element in the AX2009 XPO import form and clicking import, nothing happens. NOTHING, no error messages etc.. I started comparing an AX2012 XPO with an AX2009 XPO in notepad and found the following small changes that one can make to the AX2012 XPO in order at least initiate the compare form for an import into AX2009.

1. Tables For tables the following needs to be modified. Edit your XPO in notepad and

1.1 – Delete EnforceFKRelation 0 Line
1.2 – Delete FULLTEXTINDICES…ENDFULLTEXTINDICES Section
1.3 – Delete the Methods start and end element if you have no methods on the table.

2. Classes: In your XPO
2.1 Delete references to Event Subscriptions
2.2 Add the following line right after the METHODS line in the class node:

Version: 3

Screen Shot 2014-08-06 at 4.33.21 PM

Validating a General Journal Produces xpp Error

Problem / Symptom: When publishing a journal you receive a generic “Microsoft.Dynamics.Ax.Xpp.InfoException” was thrown error. Or you receive an error “Account number for transaction type Bank does not exist.”

image004

 

Description: We received this error reported by the client when trying to post a general journal recently. They received the generic type of XPP error as shown above and were not able to diagnose the problem correctly

Solution: The first step to resolving this issue was to receive better error messages so that the problem can be diagnosed. By disabling the “Execute business operations in CIL” option for the user for the duration of debugging. To do this:
1. Navigate to System Administration -> Users -> Options -> Development.
2. Untick the “Execute business operations in CIL” option.

After performing the above we were able to get a better description of our problem. “Account number for transaction type Bank does not exist.”
Screen Shot 2014-07-28 at 5.21.17 PM

 

We were then able to diagnose that the bank account used as the offset account did not have a “main account” number setup for it. This may have been due to faulty importing. To resolve:
1. Navigate to Bank -> Cash & Bank Management -> Bank Accounts
2. Select bank account in question, click edit
3. Expand currency management and select a main account.

Determine which users have outdated AX client versions

Problem / Symptom: You want to ensure that all your users’ Dynamics AX clients are updated to the correct rollup.

Description: You may have recently upgraded your Dynamics AX Kernel or Application to the latest Cumulative Rollup (CU), you’ve applied it to all of your AX Client installations that you are aware of but you would still like to make sure that nobody is logging into AX on an old version and potentially causing some problems. You may also be receiving an error in your AOS event log that goes something like this

Object Server 01:  Internal version mismatch. Microsoft Dynamics AX client from [MACHINENAME] (6.2.158.0) tried to attach with 6.2.1000.4051 revision of kernel.

Solution: (Applies to AX 2009 & AX2012) There is quite a simple solution to this issue.

  • Open up any Dynamics AX client connected to the AOS in question.
  • Navigate to System Administration -> Enquiries -> Users -> User log
  • Right click anywhere in the grid, click personalise
  • Click “add fields”
  • Expand “User log”
  • Select “Build number”, Click Add

Screen Shot 2014-07-25 at 4.02.20 PM

  • Close the Select Fields form
  • Close “Personalise Form”

Your User log form will now show all sessions and their build number. You can now browse and field all sessions that don’t have the same build number as your kernel. You can also further filter this form to only show the non matching records by right clicking on the Build number field and entering “![buildnumber]” (see screenshot below)

Screen Shot 2014-07-25 at 4.08.14 PM

Click on the “General” tab to find out which computer/workstation the invalid session was coming from. You can now go and run the updates.

Screen Shot 2014-07-25 at 4.10.02 PM

SSRS Report and Viewer Not displaying in AX2012 R2.

Problem / Symptoms: SSRS Reports executed from code under limited security permissions are not displaying in AX2012 R2 or above.

Description: When running an SSRS Report that is being kicked off via code from a user role with limited security permissions, neither the report nor the report viewer is displayed. No error messages are presented. Under elevated (admin) permissions the reports are displayed. The same user is able to execute normal reports via menu-items.

The debugger will fail in class: SRSReportRun.constructReportSettings on the line settings.SetWebServiceUrl(serverConfig.serverId(), serverConfig.reportServerURL());

Solution: I wasn’t able to find any specific security permissions linked to this error that would fix the security role, however I did discover that AX seems to have deprecated the use of the SRSReportRun class in favour of the SRSReportRunController class.

Old Code:

SrsReportRun srsReportRun;
srsReportRun = new SrsReportRun (‘MyReport.ReportDesign1’);
srsReportRun.init();
srsReportRun.executeReport();

New Code

SrsReportRunController controller;
controller = new SrsReportRunController();
controller.parmReportName(‘MyReport.ReportDesign1’);
controller.runReport();