Pages

Tuesday, January 27, 2015

Create QR codes from AX

It seems like the Brazilian authorities are very modern in their approach to how you report to and communicate with them. I have found several neat things in AX from the Brazil specific features.

This time I stumbled over an assembly called QRCode and I thought I should look into what that was used for. It turns out you can create QR codes from AX pretty easy. Here is an example:
static void JobCreateQRCode(Args _args)
{
    Image           image;
    container       imageContainer;
    str             url;
    EFDocQRCode_BR  qrCode;  
    
    // The url to create the QR code. Could also for example be an e-mail address
    url = 'http://www.agermark.com';
    
    // Create an instance of the QR code class
    qrCode = new EFDocQRCode_BR();
    
    // Generate a QR code for the URL and save the result to a container
    imageContainer = qrCode.generateQRCode(url);
    
    // Use AX's good old Image class to load the image from the container
    // and save it as a file
    image = new Image();
    image.setData(imageContainer);
    
    image.saveImage("F:\QrCode.jpg", ImageSaveType::JPG);
}
You can now open the file and see the resulting QR code.

It links to www.agermark.com. Which leads me to the thing I really don't understand about QR codes. For security reasons we are always told not to click an URL that doesn't seem familiar. But scanning QR codes having absolute no idea about where they lead to is perfectly alright. Go figure.

Monday, January 5, 2015

Entering a multi line control in a grid

If you add a multi line FormStringControl to a grid, and set it up to just show one line in order to keep the normal layout of the grid, you will find that the control behaves differently depending on how you enter it; by keyboard or mouse.

If you enter the control by keyboard, it behaves as expected setting the cursor on the first line of the text. But, if you enter the control using the mouse it starts out by clearing the field and making it blank. Only when you click the field again or use up and down arrows it will show the text.

Today I spent a couple of hours trying to figure out how to fix it, and all I could come up with was the following odd solution. So, I run some code when I release the mouse button after moving the cursor into the field. This helps, but doesn't quite fix the problem. To fix it entirely I have to call that exact same code again after a short wait.

So here is my code. Executed once, and then again after a short wait for 100 ms.
public int mouseUp(int _x, int _y, int _button, boolean _Ctrl, boolean _Shift)
{
    int ret;

    ret = super(_x, _y, _button, _Ctrl, _Shift);

    this.setLineFocus();

    // The same code needs to be called again after a short wait in order to make
    // the control show the right line and move the cursor to that line
    this.setTimeOut('setLineFocus', 100);

    return ret;
}

private void setLineFocus()
{
    element.lock();
    [formStringControl].lineIndex(1);
    [formDataSource]_ds.refresh();
    element.unLock(false);
}
If I remove any part of this code, the fix won't work. I don't have any good explanations about this behavior, but it works so I'm happy leaving it at that.

Thursday, January 1, 2015

Testing JSON from AX 2012

I wanted to see how JSON could be consumed from AX, and found this great article from Jonathan.

Then I needed something to test with and found the site jsontest.com.

And so, here are two examples.

The first example just returns your IP address:
static void JSONTestIP(Args _args)
{ 
    RetailWebRequest    request; 
    RetailWebResponse   response; 
    str                 rawResponse; 
    Map                 responseData; 
    str                 responseValue;
    
    RetailCommonWebAPI webApi = RetailCommonWebAPI::construct(); 
    
    request = RetailWebRequest::newUrl("http://ip.jsontest.com"); 
    response = webApi.getResponse(request); 
    
    rawResponse = response.parmData(); 
    
    responseData = RetailCommonWebAPI::getMapFromJsonString(rawResponse); 
    responseValue = responseData.lookup("ip");
 
    info(strFmt("Element name: %1", responseValue));
}

The next example returns key/value pairs. In this example first name of a person is found based on the persons last name:
static void JSONTestKeyValue(Args _args)
{ 
    RetailWebRequest    request; 
    RetailWebResponse   response; 
    str                 rawResponse; 
    Map                 responseData; 
    str                 responseValue;
    
    RetailCommonWebAPI webApi = RetailCommonWebAPI::construct(); 
    
    request = RetailWebRequest::newUrl("http://echo.jsontest.com/Agermark/Palle/Kent/Clark"); 
    response = webApi.getResponse(request); 
    
    rawResponse = response.parmData(); 
    
    responseData = RetailCommonWebAPI::getMapFromJsonString(rawResponse); 
    responseValue = responseData.lookup("Kent");
 
    info(strFmt("Element name: %1", responseValue));
}

Thursday, December 18, 2014

Hide the Export button from the report viewer

I did a little investigation today into how you can work with the report viewer toolbar.

One practical example is to hide the "Export" button.

First add a reference to: Microsoft.ReportViewer.WinForms

Next, add this code to be called somewhere from the report viewer form:


What I really would like to do, was to add some buttons to the toolbar. But I didn't get that far today.

Tuesday, November 25, 2014

Monday, November 17, 2014

Thoughts on validateField

Here is a couple of thoughts on how to write validateField methods in the best possible way.  The validateField method is automatically called when you change a field value of a record in the client.

There are two things about validateField, that I haven't been able to figure out:

  • Is the method originally intended to be called only when a field value is changed, or should you also be able to call it to validate the current value?
  • Why do AX 2012 also have a name based validateFieldValue method?

I raise the first question because the standard AX data consistency check actually calls validateField on existing data to check the present value of the fields. That gives a number of errors from standard AX, that really isn't errors but appears so because the code on the field assumes you have modified the field value from something else to the present value.

One example of this, is the many errors you get from Currency:



In this case you could add code to see if the value actually is changed.

Also some validations should check that the field actually has value. One example from standard AX of a validation not doing this is license plate validation on warehouse locations, where a blank value in some fields will give you this error:



Here the validation looks up another location using a blank value, and the returned flag for License Plate Control will of course be blank.

Don’t update data in a validation method. You should always be able to call a validation method without risking any data to change in the system. Changes should be moved to the modifiedField, modifiedFieldValue, update or insert method.

Don't display any dialogs from the table based validation method without checking if the validation method is being triggered from a form data source. The code analysis tool on LCS will report this as a Best Practice issue, as the validation could be called from a server tier. Consider moving these dialogs to the forms where needed.

Friday, November 14, 2014

Search available icons from embedded resources

I was today made aware of this clever searchable cross reference like tool for finding places where image resources are used in AX:


You can read more about the tool and download it here: http://axaptanotes.blogspot.com