Monday 29 October 2012

Dynamics AX 4 report - Unable to load page

One of the very old question pop up again recently - the "Unable to load page" on AX4 report.
The last I know, it need a hotfix on the Dynamics AX client (AX32.exe).

I had a quick check on local instance with different client build (4.0.2501.116 and 4.0.2503.998), one has the error and the other doesn't.


  • App: 4.0.2501.116 with Client: 4.0.2501.116 => "Unable to load page" error on report
  • App: 4.0.2501.116 with Client: 4.0.2503.998 => No error on report






Wednesday 24 October 2012

Import with ID values will retain ID from other layer


More details on layer & ID
http://msdn.microsoft.com/en-us/library/aa548140(v=ax.50).aspx
http://msdn.microsoft.com/en-us/library/aa881527(v=ax.50).aspx


Any object created in VAR layer usually has an ID range between 30001 - 40000.
Eg. 30001, 30002, 30003 ... etc.

This is true if the object is CREATED from that layer, but not if you created it by IMPORT with ID.

Let's say, you created a field in USR layer, export with ID, delete from USR layer, then import with ID into VAR layer. When the new field is imported into VAR layer with ID, it doesn't create a new ID, but it do as the option says - Import the ID, so, you'll get USR ID in VAR layer.

Eg (screenshots below):

  1. A new field 'ReadyToPost' is created in USR layer at CustInvoiceTable
  2. Export the table, then delete if from USR layer
  3. Login to VAR layer and import with ID
  4. You'll then see the new field 'ReadyToPost' created in VAR layer, but having USR layer ID.

Option - Import with ID
USR layer ID retained - 50002




Dynamics AX services login & SQL access/security risk

Dynamics AX AOS service login require only a few access and this would prevent developer doing some funny thing around the database. But sometimes when DBA doesn't give a throughout thought on it, it might cause security issue.

Eg.
DBA is requested to look at an AOS start up issue, rather than checking and fixing it properly, a sysadmin role is granted to the AOS service login as a quick fix. Sometimes under urgency or pressure, this might be a quick fix, but it would cause security risk issue. Although this role is granted only to the AOS service login, but developer with AOT access can issue a direct SQL statement to grant themselves the sysadmin role now.

Running the job below would grant the sysadmin role.

static void UpdateSQL(Args _args)
{
    str                             sql;
    Connection                      connection = new Connection();
    SqlStatementExecutePermission   sqlPerm;
    Statement                       statement;
    int                             rowsAffected;
    ;

    try
    {
        sql     = "EXEC master..sp_addsrvrolemember @loginame = N'Domain\\Username', @rolename = N'sysadmin'";
        sqlPerm = new SqlStatementExecutePermission(sql);
        sqlPerm.assert();

        statement    = connection.createStatement();

        rowsAffected = statement.executeUpdate(sql);

        CodeAccessPermission::revertAssert();
    }
    catch
    {
    }
}

Friday 12 October 2012

Clone BitLocker partition on Windows 7 with Macrium Reflect

Received my SSD this afternoon and cloned the HDD partition over. It took few hours to complete the clone but it is well worth it, my old laptop feels like a new one now.

Before purchase the SSD, I've seen quite a number of post regarding the concern and issue on cloning BitLocker partition on Windows 7. It seems like a lot of people hitting error or unable to boot up after the cloning. I'm not getting into details about that, but just to share my experience on replacing my HDD with SSD.

I bought the Samsung SSD 830 256GB (SATA III) from Amazon.co.uk, free shipping and arrived in just a few days time. The package has a SATA to USB adaptor, which is quite useful to me (I don't have a caddy for secondary HDD in my laptop). It also comes with Norton Ghost 15 full version, but I didn't use it. For simplicity reason, I used Macrium Reflect. Below are the steps taken.

*I'm using Windows 7 with BitLocker enabled for OS partition
  1. Boot up OS as usual, install Macrium Reflect
  2. Plug in the SSD to USB port (with the supplied SATA to USB cable)
  3. Run the Macrium Reflect software and clone the partitions from HDD to SSD
    Cloning all 3 partitions at one go:
    > the reserved partition (~180MB)
    > the recovery partition (~2GB)
    > the OS partition (the remaining of my drive size)
    *Just Google for the instructions or video in YouTube
  4. Let it run for few hours
  5. After the cloning is completed, shut down laptop
  6. Took out battery, swap the HDD with the new SSD, put the battery back in
  7. Boot up OS as usual
  8. Turn on BitLocker again
No issue during the whole process, it all gone well.


Tuesday 9 October 2012

Trick to add included column in AX2009

Ref. and more details at: http://daxdilip.blogspot.com/2011/05/tip-how-to-avoid-overriding-of-sql.html

In brief, included column is not supported in AX2009, but is now supported in AX2012.
To work around that in AX2009, it is to write the included column SQL statement and fire it up at the end of the AX synchronization (\Classes\Application\dbSynchronize).

You'll need to put some thought on the pro and cons before implement it, a proper control procedure should be put in place due to other developer from your project team might not aware of this extra SQL script.

Pro:
- Included column available in AX2009

Cons:
- Maintenance of the index
- Overhead during synchronization (index will first be dropped then recreated)

*Note
- New index created outside of AX (directly into the table in SQL) will be dropped during synchronization
- Included column added directly from SQL into existing AX table's index will be dropped only if you made changes to that index in AX AOT node

Alternatively, Advanced Storage Management seems to offer this feature, I've never try it before, but thought just mention it here for anyone who looking for options.
URL: http://www.aximprove.co.uk/product_AdvancedStorageManagement.html

Monday 1 October 2012

try...catch... block not executed in transaction pair (ttsbegin/ttscommit)

One of the common habit to start developing on class that extends RunBaseBatch is by duplicating the Tutorial_RunBaseBatch class. The run method has a ttsbegin & ttscommit, developer usually put codes within that section, this normally works OK. But sometimes when this class is calling a lot of other class to do some job and that class has a well developed error handling, this outer ttsbegin & ttscommit might break it.

One of the case I seen recently is the use of SalesFormLetter.
The SalesFormLetter class has its own error handling, if it hit an error for current record (Eg. Sales order), it will handle the error and then move on with the next record. But this error handling is broken by another batch job development (a pair of outer ttsbegin & ttscommit).

Below are the details:

  • Developer duplicate the Tutorial_RunBaseBatch class
  • Modify it for a batch job development by putting them in .update() method and this method is wrapped by a pair of ttsbegin & ttscommit
  • This development perform some business logic and at the of of the process, calling SalesFormLetter class to do Picking list (Eg. Picking list of many different sales order)
  • A few orders processed, pick list printed
  • After several orders are posted, it reached an order that cause error
  • The error jump out to the inner most try...catch... block that's outside of the ttsbegin & ttscommit, this cause the try...catch... block inside SalesFormLetter not catching it, hence, not moving on to next order and posting stopped
  • All previously posted picking list is rolled back
  • Warehouse guy try to pick the item but picking list not found in the system (due to rolled back)

This issue happen because any error occur within a ttsbegin & ttscommit pair will not be catch by the try...catch... block within the transaction pair, it will jump out to the inner most try...catch block outside of the transaction pair.

Refer to this link for more details: http://msdn.microsoft.com/en-us/library/aa893385(v=ax.50).aspx

Example below shows an error occur in SalesFormLetter will not be catch by its try...catch... block, instead, it will jump out to the try...catch... block at the .run() method.

//Code modified to make this easier to read, but should
//roughly explains what this article is trying to achieve

public void run()
{
    try
    {
        ttsbegin;
        this.update();
        ttscommit;
    }
    catch
    {
    }
}


public void update()
{
    SalesFormLetter salesFormLetter;
    ;

    //Eg. Some business logic for batch job
    this.someOtherMethod();
    this.someOtherLogic();

    //Eg. Post a few picking list at the end
    salesFormLetter = SalesFormLetter_PickingList::construct(true);
    salesFormLetter.proforma(false);
    salesFormLetter.printFormLetter(true);
salesFormLetter.updatePrinterSettingsFormLetter(..., ...);
    salesFormLetter.parmMarkedLines(true);
    salesFormLetter.parmPostBatchMode(true);
    salesFormLetter.parmInventLocationMarked(...);
    salesFormLetter.update(...);
}


When developing error handling, it is important to consider the other classes that will be called and investigate further whether it will break it or develop a mechanism to handle it properly.