Wednesday, December 7, 2016

A dynamic query range for month to date (SysQueryRangeUtil)

Here is the code for creating a month-to-date filter:

public static str monthToDateRange(int relativeDays = 0)
    utcdatetime currentDateTime;

    currentDateTime = DateTimeUtil::applyTimeZoneOffset(DateTimeUtil::getSystemDateTime(), 

        DateTimeUtil::date(DateTimeUtil::addDays(currentDateTime, relativeDays)));

The code must be added to the SysQueryRangeUtil class.

Use it like (monthToDate(n)) where n is the number of days from today. So month-to-date-yesterday would for example be (monthToDate(-1)).

Thursday, December 1, 2016

First place to look when batch seems not be working i AX7

When you can see that the batch service is not really picking up any jobs, the first place to look is normally in the batch group setup. Here you'll validate that the different batch groups are connected to a batch server.

And here comes the fun part where AX7 might trick you, if you not on your toes. The selection panels for Selected and Remaining servers have switched places since AX 2012...

Here is the dialog in AX 2012:

And here it is in AX7:

A thank you goes out to Lau Larsen for pointing this out.

Thursday, November 24, 2016

Difference between "Local" and "Remote" settings in the "Run mode" property of a form part

Recently I was wondering what the difference between the "Local" and "Remote" settings in the "Run mode" property of a form part actually meant. I couldn't find any documentation on the wiki.

In a Microsoft Yammer community Harold Albrich of terna GmbH pointed me to an earlier answer in the same forum. Since this blog also serves as my own notebook, I'll share my version of the answer here:

When the form part is called as Remote, there is no coupling between the form part and the caller form. So no linking of data sources and also no element.caller() object. And as Remote the form part is executed in a separate session.

With Local it is the other way around. You can link data sources and you can work with element.caller().

Tuesday, October 25, 2016

SQL Server trial license expired on your cloud boxes for AX7?

If you have made deployments before summer, you might have deployments where the SQL Server is installed with a trial license and it is set to expire november 1st. When it expires you are not able to start the SQL Server service anymore.

Here are the necessary steps to update SQL Server:

  • Obtain a valid installation media and license. 
  • Stop the "Hyper-V Time Synchronization Service" service.
  • Change the system date on the server, to a date before the trial expiry date.
  • Run SQL Server installation and choose "Upgrade from preview version"
  • Start the "Hyper-V Time Synchronization Service" service again.

The reason for changing the system date is that the upgrade won't run if it can't start the existing SQL Server service.

Wednesday, October 5, 2016

Why you can't find any delete actions for the DocuRef table in AX7

There aren't any...

In AX7 clean up of records in DocuRef is handled by a recurring batch job. The batch job is automatically created during AX initialization when you deploy a new solution. The batch job is listed as "Scan for orphaned document references" and it is scheduled to run every sixth hour.

I can't find the job between the batch jobs in the demo data though. But you can add it by calling the loadData method of the class DocumentManagementSetup.

Thursday, September 29, 2016

Setup a keyboard shortcut on the WMS mobile device portal

You can setup keyboard shortcuts for the mobile device portal without any code changes at all. Here is in short what you need to do.

You need to know two things before you can setup the shortcut:

  1. The HTML id of the button type to create the shortcut for.
  2. The JavaScript keyboard code for the key you want to use.
You find the HTML id of the button type by looking at the HTML source behind it. In Internet Explorer you right click a button of the type and select "Inspect element":

Then you can find the HTML id here:

The JavaScript keyboard code you can find from this menu item (sorry, the screenshot is in Danish, but it should say something like "Show codes for keyboard short cuts"):

When you have clicked the menu item, just hit the key you want to use and it will show the keyboard code:

Here I have hit the escape key and got code 27 for that.

Now you need to decide how to show what the keyboard shortcut is shown as. That is added to text of the button. In this case I'll show it as "ESC".

To enable the escape button  for "Cancel", I can enter the following setup in "Work user mobile display settings":
So this is "HTML id"("text to show in the button")="keyboard code"

And it'll look like this:

Here is a good article from Microsoft explaining the setup:

Friday, September 2, 2016

The Microsoft Dynamics AX Help Wiki is getting a lot of good content these days

The last few days and weeks a lot of good documentation has been added to the Wiki. If you want to follow new articles you should subscribe to the RSS feed.

And by the way, a lot of the X++ documentation also applies to older versions than AX7.

Friday, August 12, 2016

Don't change the name of a Form instance in AX 7

In AX 2012 you could do something like this, where you change the name of a Form instance:

form = new Form(formstr(MyPrettyForm));"AnotherNameToConfuseEverybody);

args = new Args();

formRun = classfactory.formRunClass(args);

In AX 7 changing the name seems to confuse AX to a sense where it can't figure out to actually run eventual code in (at least) the init and run methods of the form.

For many reasons I think it is a poor idea to change the name of a form instance, now there is also a technical reason not do it.

Wednesday, August 10, 2016

Public members vs. Parm methods on classes extending SysAttribute in AX 7

I thought I did the right thing when I changed attribute classes that I moved into AX 7 from AX 2012, so they had public members instead of Parm methods.

Well, don't do that. As it turns out the attribute framework is looking for the Parm methods when trying to apply the member values. If you don't have them, the values are not being set.

Hopefully this information can save you an hour or so of tedious debugging.

Wednesday, July 20, 2016

Moving label files between models in AX7

When you need to move label files between models in AX 7, there are a couple of thing to remember.

  • A label file consists of several files.
    Under \AxLabelFile there is an XML file for each language with some metadata about the label files.
    And then again under \LabelResources you'll find the actual label files for each language.
  • In the XML files with the metadata about the labels your need to modify the RelativeUriInModelStore property to reflect the new location.
  • You should move the above files from VSTS, so you keep version history of earlier changes.
  • If you did an earlier build, you must manually remove the compiled dll's for earlier compiled labels files. You'll find these in the Resources folder for the model you moved the files from. There is a dll directly in the folder, and one in the subfolder for each language. If you don't remove the dll's, AX might keep using the old and wrong version.  

You might want to move the label files from under ApplicationSuite, where the LCS upgrade tool puts your custom label files, into a new extension model.

Thursday, June 23, 2016

One of those functions...

Now and then you come across AX functions you haven't heard about or that you have never really used. The lack of usage is often linked to the lack of documentation, but that's another story.

Today I came across the formattedStr2Num function. It is able to take a formatted string with some arithmetic and do a calculation of the expression. Something I have often seen developers write code to pass through the compiler and run the compiled code to do.

Here is a small example of how to use it:

static void formattedStr2NumDemo(Args _args)
    str s = "(8 + 2) / 2 * 4";
    info(strFmt("%1", formattedStr2num(s)));

The function actually also pass the expression through the compiler, but in a controlled way so you can't just run any old arbitrary (dangerous) code through it.

If you combine this with a dynamic strFmt call, you have yourself a small expression calculation engine.

The timing for me to figure this out is awful, since the function is deprecated in AX 7. From AX 7 you could instead use something like ADO.NET's expressions parser in DataTable.Compute.

Thanks to Marco Scotoni, Johan van Veldhuizen and of course Peter Villadsen for being smarter than me and learning me this today through the Yammer group we share membership of. And I hope I wasn't the last AX developer on earth to learn about this function.

Monday, June 13, 2016

Cross-company data sharing in AX7

In Update1 a new data sharing feature has been added. You can use the feature to share simple group and reference data between companies in an AX deployment.

You can read more about the feature in the Microsoft Dynamics AX Wiki.

From a technical perspective, it is interesting to see how the setup generates triggers on the database to register changes and to replicate data. These also indicates the caveat; data is replicated on SQL level and AX business logic is not executed.

Thursday, May 12, 2016

A dynamic strFmt call

In the communities a users asked about a way to make a dynamic strFmt call, not knowing the number of parameters up front.

With some good critical tests from Levgen Miroshnikov (blog) I think I have managed to write a class method that does that.

Regular StrFmt will round decimal value and change the decimal point to whatever fits your windows locale. This method has a parameter allowing you to keep the formatting of decimal values.

Here is the code for it:
public str dynamicStrFmt(
    str         _expression,
    container   _args = conNull(),
    boolean     _keepRealsDecimals = false)
    str         ret;
    Counter     counter;
    str         argStr;
    char        argChar;

    if (_args == conNull())
        return strFmt(_expression);

    for (counter = 1; counter <= strLen(_expression); counter++)
        if (subStr(_expression, counter, 1) != '%')
            ret += subStr(_expression, counter, 1);
            // This is a point in the string where an argument starts
            // Find the end of the argument
            argStr  = '';
            argChar = subStr(_expression, counter + 1, 1);
            while (argChar && argChar == strKeep(argChar, '0123456789'))
                argStr += argChar;
                argChar = subStr(_expression, counter + strLen(argStr) + 1, 1);

            // Lookup and insert the value to the result
            if (argStr)
                // Check that the argument is within the bounds of the number of supplied argument values
                if (str2Int(argStr) <= conLen(_args)) 
                    if (typeOf(conPeek(_args, str2Int(argStr))) == Types::Real && _keepRealsDecimals)
                        ret += num2str(conPeek(_args, str2Int(argStr)), -1, numOfDec(conPeek(_args, str2Int(argStr))), 1, 0);
                        ret += strFmt('%1', conPeek(_args, str2Int(argStr)));
                    // The argument is not within the bounds of the supplied argument values. Keep the argument unchanged.
                    ret += subStr(_expression, counter, 1) + argStr;
                // Move the current position in the _expression string to a position after the argument
                counter += strLen(argStr);
                // Didn't find the argument to be meningfull, so just add the argument character as-is
                ret += subStr(_expression, counter, 1);

    return ret;

And a main method for a quick demo:
public static void main(Args _args)
    aaaStrFmtBuilder    demo = new aaaStrFmtBuilder();

    info (demo.dynamicStrFmt("%1 + %1 = %2", [2, 4]));
    info(demo.dynamicStrFmt("%1/(%2*%1)", [1000, 0.135], true));
    info(demo.dynamicStrFmt("%2 %3 %1", ['AX', 'Microsoft', 'Dynamics']));

And here you can download a project with the class and unit tests for it:

The two classes are prefixed with 'aaa', making it easier for you to find them in the AOT :-)
Please change the names if you are going to use the code for something real.

Monday, May 9, 2016

Use Visual Studio to merge code for an AX 2012 CU

Here is a great explanation about how you use Visual Studios merge tool to merge files:

You can find of plenty other tools for compare and merge on the Internet, but you probably already have a Visual Studio license. Here is an overview of other tools:

Thursday, March 31, 2016

Check out multiple languages of a label file in one process in AX 2012

If you work under version control and you update labels in more than one language, you might have found it cumbersome that you for each language need to switch language in the label dialog and then check each language out individually.

I have made a job that does this in one process. The job uses saved selections from the label dialog to figure out the label file to check out, and in which languages.

Use at your own risk.

Sunday, February 28, 2016

What's new in the Dynamics AX user experience - Feb 2016

This is an excellent Office Mix on the latest news about the UI in the new Microsoft Dynamics AX:

Tuesday, February 9, 2016

Dynamics Technical Conference 2016 - Deep Dive Workshops

Microsoft still has some open seats for the Deep Dive Workshops that will be run after the conference.

All of the Deep Dive sessions will showcase the new Microsoft Dynamics AX ('AX 7').

Here are the details for the deep dive sessions – all will be held at the Microsoft Conference Center (Building 33) on the Microsoft campus in Redmond, Washington:

Microsoft Dynamics Retail Conference 2016 – 2 days of L300 learning from the R&D Retail Team on the new Microsoft Dynamics AX (‘AX 7’):

  • The deep dive includes presentations across the entire Retail solution (Modern POS/Cloud POS, Retail Server, Hardware station, eCommerce, Catalog & Call Center sales, Merchandising, BI/Reporting), as well as presentations highlighting solution architecture, they complete retail application lifecycle with Lifecycle Services (LCS), customization and extensibility, performance and scalability, troubleshooting, and best practices.
  • Hands-on lab experiences will include omni-channel scenarios, retail application configuration, setup, and deployment, as well as extensibility, monitoring, troubleshooting, and reporting.
  • Please note that content presented at the Dynamics Technical Conference is considered a prerequisite and will not be repeated during the Retail Conference. Attendance at the Dynamics Technical Conference is a mandatory prerequisite for the Retail Conference.
  • You can register here for the Retail Conference 2016:

Advanced Presales Workshop for Microsoft Dynamics AX (‘AX 7’) – 3 days of going deep on presales

  • This workshop provides presales consultants with solid grounding for demonstrating Microsoft Dynamics AX as a solution that is superior to competitive products. Participants learn through typical business scenarios, starting with foundational knowledge and persona scenarios along with exercises and case discussions by using real-world examples. The workshop covers the range of capabilities in Microsoft Dynamics AX – the learnings provide a basis for future work in vertical industries.
  • The workshop is designed for consultants who have led or had a strong supporting role in previous AX projects.
  • You can register here for the Advanced Presales Workshop:

Budget Planning Workshop for Microsoft Dynamics AX (‘AX 7’) – 3 days of intensive learning on budget planning

  • This workshop helps senior consultants to plan configure,  and implement advanced budgeting scenarios. The workshop engages with organizational hierarchies, security models, workflows, and integration to Excel for working with budget plans. The workshop is based on typical business scenarios and covers planning and setup, templates, and simulation as well as budget functions and tools with real-world examples.
  • The training is intended for architects and senior consultants with proven experience on AX implementation and solid knowledge of AX financials, as well as pre-sales consultants focused on financials.
  • You can register here for the Budget Planning Workshop:

Friday, January 29, 2016

pickTable method being painfully slow in AX 2012?

The call to pickTable in a user session can be extremely slow. I have measured times of up to 20 (!) minutes on an otherwise well functioning system.

A trace shows very clearly that the method is very chatty, so wrap the call into a server bound method. And ta-da, what took minutes now takes a second or two.

Wednesday, January 27, 2016

Explore the different form arrangements in AX 7

There is a tutorial form in AX 7 that shows you the different types of form arrangements.

The link to the form is: https://(your AX instance)

Tuesday, January 19, 2016

Show the table browser in AX7

I just wanted to share this tip I have picked up from Marc Hugelshofer in a forum.

In your browser you can open the table browser by entering an URL that looks something like this:

https://(the url of your ax instance)/Default.htm?mi=SysTableBrowser&prt=initial&cmp=USMF&tablename=CustGroup&limitednav=true

Thursday, January 7, 2016

Creating code for a dll that might or might not be on the system

We are working on a vertical solution where parts of it requires integration with an external .net dll. Not all customers are interested in using the part that requires the external dll, and thus they are not interested in installing the dll.

So how can we write our code so it will compile and run regardless of this dll being there or not.

Well, one way is to create a proxy class in AX to wrap all operations for the external dll. The proxy class need to call the dll through CLRObjects which makes it possible to compile the code even if the dll is not present. Secondly the proxy class needs to runtime deal with the fact that the dll is not there.

The following job shows how the proxy class could be constructed. The example works with a random standard dll I choose.

static void TestCLR(Args _args)
    CLRObject /*Microsoft.Dynamics.AX.Framework.Tools.DMF.CrossInstanceProxy.InstanceProxy*/ dllObject;

    str     nameOK     = 'Microsoft.Dynamics.AX.Framework.Tools.DMF.CrossInstanceProxy.InstanceProxy';
    str     nameNotOK  = 'xMicrosoft.Dynamics.AX.Framework.Tools.DMF.CrossInstanceProxy.InstanceProxy';

    str proxyStr = nameNotOk; // Switch name here to test with the dll being alright and the opposite

        dllObject = new CLRObject(proxyStr);

    catch (Exception::Internal)
        // Clear the info log of CLR errors
        infolog.clear(infologLine() - 2);
        // Register that the dll couldn't be instantiated
        print "Could not make an instance of the dll";

    if (dllObject)
        print "Got the object";
        print "No instance";

It is a pest working against just the CLRObject and not with the direct access you could get if you could be sure that the dll would be there. That's a another reason for wrapping all your calls to the dll into an X++ proxy class.