Pages

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 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);
        }
        else
        {
            // 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);
                    }
                    else
                    {
                        ret += strFmt('%1', conPeek(_args, str2Int(argStr)));
                    }
                }
                else
                {
                    // 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);
            }
            else
            {
                // 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: https://roadtoalm.com/2013/10/22/use-visual-studio-as-your-diff-and-merging-tool-for-local-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: https://en.wikipedia.org/wiki/Comparison_of_file_comparison_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: