Thursday, 4 February 2016

WHS Mobile Device application debugging

This post is about my debugging and error finding in the WHS Mobile Device application.

First of all some general info about the Warehouse Mobile Device Portal, Microsoft published a series of post on the topic that will cover the architecture, setting up etc. The posts are well worth the read.
Warehouse Mobile Device Portal part 1
Warehouse Mobile Device Portal part 2

and a general post about my problem area

Inventory on-hand movements/transfers using the mobile device menus

First of, there is the option of simulating the web based client by opening the Form WHSWorkExecute:

when opening it you get a X++ form that is ideal for debugging the code - the menu is customizable - so your may look different


And now to the problem, when ever we did a Location transfer (that was setup as a movement as per the guides in the Microsoft blogs above - so it's not the standard naming) in the mobile device application it went back to the From page.

We could copy the license plate into the client and the rest of the data would be filled in - seemingly:



Once we pressed the OK button however, the client would skip back to the From-page again, deleting the Qty:



Since the environment is without customisations, we suspected that the issue was data related - furthermore we could tell that the Inventory status was apparently blank even though we confirmed the status to be available in the On-hand inventory.


We could do transfers in the AX client via the Transfer journals, so the data related to the item was correct.

We were interested in investigating how the client form (either the web based or the AX form) got populated, because somewhere along the lines, the inventory status went missing.

Well, after some extensive digging we found the class that populates the form with labels, data fields and buttons:

WHSRFControlData



The population takes place in the populateData() and we could confirm that the InventStatusId was correct when assigning to the fromInventStatusId variable
 
but when adding the Id to the control, there was a round trip to the WMSInventStatus where the Name was inserted as data value and not the Status from the actual inventory status.

A short look into the WHS Inventory status form showed that the Name was blank, resulting in the Inventory status of item in the Mobile Device Portal not being populated - and consequently resulting in the user not being able to transfer the item (and the behaviour above).
 

Adding the Name to the Inventory status resulted in expected behaviour of the forms and we could do our transfer


Wednesday, 6 May 2015

Finding X++ compile errors from CIL compile log

Every once in a while you will get some strange errors in your code - in the pre-2012 R3/R2 releases of AX without the axbuild command, doing a full X++ compile is quite time consuming.

As known, without an error-free X++ compile, the CIL compile will fail, so how can developers easily find any X++ errors if the CIL compile fails? Sure, do the full X++ compile, but wait, there is more.

A quick way to find X++ errors after an unsuccessful fuld CIL generation is to look in the CIL generation log.

The log can be found on the AOS-server in the relevant AOS-instance's program files, e.g.

"C:\Program Files\Microsoft Dynamics AX\60\Server\[Instance name]\bin\XppIL\Dynamics.Ax.Application.dll.log"

You want the log to look like this

Friday, 17 April 2015

Assigning enum value to variable from enum name

Working with utilities there are alot of file exchanges - alot.

Today  we got a strange case though. In a file we receive a value that represents the name of a enum value, e.g. E17. The label of the enum value is Consumption, so we wanted to assign the value to a field value in a given table, so this is what we came up with

    // must assign Consupmtion and not E17
    JournalConnection     journalConnection;
    str                   typeOfMeteringPoint = "E17";
   
    SysDictEnum             dictEnum    = new SysDictEnum(enumNum(ConnectionTypes));
    ;
   

    journalConnection.type = dictEnum.symbol2Value(typeOfMeteringPoint);






we also came up with this one where we inserted the option of a small if-statement to check if the name of the enum value is found on the enum:
    
    JournalConnection           journalConnection;
    ConnectionTypes             connectionTypes;
    str                         typeOfMeteringPoint = "E17";
   
    SysDictEnum             dictEnum    = new SysDictEnum(enumNum(ConnectionTypes));
    Counter                 values      = dictEnum.values();
    int                     idx;
    ;
   
    for(idx = 0; idx< values ;idx++)
    {   
        if(typeOfMeteringPoint == dictEnum.index2Symbol(idx))
        {
            journalConnection.type = str2enum(ConnectionTypes,dictEnum.index2Name(idx));
        }

    }

Tuesday, 31 March 2015

Primary legal entity primary address and country code specific menu items

Earlier this week a customer experienced that a very important menu item all of a sudden disappeared. It was quite disturbing since it is the call center's main entry point for calls.

I could see the menu item in AOT > Menus but when I opened the element I got this error message:


I knew that the Menu Item in question had a CountryRegionCode property set to Denmark (DK)
and that the License configuration had Denmark set in Country/Regional specific feature:

My immediate thought was that the customer had altered the role for the users, however when I logged on to the clients environment with the System Administrator role, the menu item still was unavailable.

So, what was the problem? I could confirm that it had something to do with the CountryRegionCode by doing some tests.

With AX 2012 the controlling party is defined by the organization model. "If the country context of the controlling party matches the country context of the controlled entity, the functionality or UI elements will be enabled." (MSDN-article "Applying Country Specific Functionality [AX 2012]")

So the country region code of the primary address of the legal entity defines the country/region context for the controlling party - which means that if there is no primary address or the primary address is not in the same country as the menu item, the UI element will not be shown.

The resolution was to check if the Legal entity had Denmark as country code for the primary address:


Open the Legal entities form:



which it did not - they had altered the country region for the primary address for no reason.


Once the primary address' country region was edited back to Denmark, the menu item appeared again.

Thursday, 5 March 2015

Microsoft Dynamics AX 2012 R3 and Azure

Back in November 2013 Mary Jo Foley reported that Microsoft planned to Dynamics AX to the clould by spring 2014.

With the release of Dynamics AX 2012 R3 Microsoft introduced the ability of deploying AX 2012 R3 to the Cloud, proving once again that Mary Jo Foley's articles are well worth reading when it comes to news on Microsoft and their products. While Microsoft have updated their articles on how to plan a AX deployment to the cloud, the move to a cloud based solution may seem a challenge if you never before have investigated Azure.

In order to get the fundamentals in place, Microsoft have published a free e-book. It is a great read, not only covering many of the topics that are relevant for a Dynamics AX 2012 R3 implementation but will also covering areas for general development.

Download your copy from:
http://blogs.msdn.com/b/microsoft_press/archive/2015/02/03/free-ebook-microsoft-azure-essentials-fundamentals-of-azure.aspx


Tuesday, 3 March 2015

Videos from Technical Conference 2015

The annual Microsoft Dynamics Techical Conference took place back in February. As always the conference demonstrated Microsoft's strong commitment to the Dynamics Product portfolio and gave participants the opportunity to hear about practical and technical content, as well as recommended best practices, delivered by Microsoft leaders and technology experts.

The videos from the conference are now available from Customer and Partner Source site:


Saturday, 28 February 2015

2 examples of importing multiple files

In AX, the typical way to import files via the UI is to do it one file at the time. If you have multiple files to import there are several options on how to do this via the AIF.

Every once in a while, it could be useful for the end-user to have the option of importing several files through the UI.

I have found two similar examples on how to achieve this:

Example 1)
static void MultiFileSelectDlgTest_1(Args _args)
{
    System.Windows.Forms.OpenFileDialog ofd;
    System.String[]                                          s2;
    int                                                               counters;
    str                                                               imageValaue;
    int                                                               i;
    ;
    ofd = new System.Windows.Forms.OpenFileDialog();
    ofd.set_Title("Select files");
    ofd.set_Multiselect(true);
    if (ofd.ShowDialog() == System.Windows.Forms.DialogResult::OK)
    {
        s2 = ofd.get_FileNames();
        counters=System.Convert::ToInt32( s2.get_Length());
        for(i = 0 ; i <= counters-1; i++)
        {
            imageValaue = System.Convert::ToString(s2.GetValue(i));
            info(strFmt('%1', imageValaue));
            //do whatever you want ;)
        }
    }

}
The dialog will show this:
and the output in the infolog shows the individual files processed:


Example 2) The second example is just another flavour of the first, adding a filter option on the input content.

static void MultiFileSelectDlgTest_2(Args _args)
{
    int             idx;
    int             cnt;
    boolean         result;
    System.String[] files;
    Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();

    dlg.set_Multiselect(true);
    dlg.set_DefaultExt(".txt");
    dlg.set_Filter("Text documents|*.txt|All files|*.*");

    result = dlg.ShowDialog();

    if (result)
    {
        files = dlg.get_FileNames();

        cnt = files.get_Count();

        for (idx = 0; idx <= cnt; idx++)
        {
            info(files.get_Item(idx));
        }
    }
}