A blog offering various X++ code snippets and tips for Microsoft Dynamics AX

Friday, April 24, 2009

Dynamics AX now on Microsoft Surface

In the following link you can see a video about Dynamics AX warehouse command centre on Microsoft Surface:

Click Here

Cool!

If you any questions or comments e-mail me on: mirko@mirkobonello.com

Thursday, April 09, 2009

AX 2009 Workflow

Today I started exploring AX 2009 Workflow by following the Appendix A of the Development I in the Microsoft Official Training Manuals.

The version of my manual had some missing steps, these mainly being:

a missing (but important) code snippet for creating the main() method for the 'Submit to Workflow Class' which I then found on MSDN here (scroll down until you find it);

failure to make you create a subject; and

it assumes that AX 2009 SP1 already has a 'Workflow Configuration' Menu Item for the Projects Module but I had to create it manually (in the process I discovered that the Bug in AX 2009 RTM, which only scans the sys layer for new work flow items, is now solved in SP1).


If you any questions or comments e-mail me on: mirko@mirkobonello.com

Tuesday, April 07, 2009

Very nice quote

"You see, developing software is a process. The consumer will typically see the end product as if it magically popped out of thin air. But it didn't magically appear. There were meetings, specs, coding sessions, coffee, code reviews, debugging sessions, coffee, testing, explanations, eureka moments, coffee, and so on ..."

Inspired from the book "Inside Microsoft® Exchange Server 2007 Web Services" by David Sterling; Ben Spain; Michael Mainer; Mark Taylor; Huw Upshall.

Thanks for this nice quote :)

If you any questions or comments e-mail me on: mirko@mirkobonello.com

Passed First AX 2009 Dev Exam

Today I passed the AX 2009 Introduction to Development - MB6-819 exam. This was my first AX 2009 exam and my 12th Microsoft Exam :-)

If you any questions or comments e-mail me on: mirko@mirkobonello.com

Tuesday, February 03, 2009

Table buffers: Deep Copy vs Shallow Copy

Some people ask:

"What is the difference between using the equal assignment sign '=' between buffers and using the using the .data() method?".

It's a question of deep copy and shallow copy.

In the following X++ code snippets follow the RecId number of the buffer called currentSalesTable. Please ssume the RecId of the currently selected record in the datasource is 100 and that the current selected record is not the first (top) record in the form.


void exampleMethod()
{
SalesTable copySalesTable;
;
// copySalesTableRecId = 0
copySalesTable = SalesTable;
// copySalesTableRecId = 100

// do some processing

SalesTable_ds.executeQuery();
// SalesTable RecId now changed to 90

// copySalesTable RecId = 90 i.e. shallow copy
info(num2str(copySalesTable.RecId, -1, -1, -1, -1));
}



void exampleMethod()
{
  SalesTable copySalesTable;
  ;
  // copySalesTableRecId = 0
  copySalesTable.data(SalesTable);
  // copySalesTableRecId = 100

  // do some processing

  SalesTable_ds.executeQuery();
  // SalesTable RecId now changed to 90

  // copySalesTable RecId = 100 i.e. deep copy
  info(num2str(copySalesTable.RecId, -1, -1, -1, -1));
}


Therefore as you saw from the above code snippets the .data() method is actually a deep copy while the assignment '=' operator is just a reference because when the datasource 'SalesTable' buffer changed, in the first code snippet, the 'copySalesTable' buffer also changed while in the second case the copy kept its first RecId value.

However, there is an exception to this rule. This rule does not work if you update the original buffer (SalesTable in the above code - the buffer I'm copying from, in the code below i.e. originalSalesTable) with a shallow copy.

Consider the following code snippet and its comments:

static void Job6(Args _args)
{
  SalesTable originalSalesTable,
      copySalesTable,
      copySalesTable2;
  ;
  select firstOnly originalSalesTable;

  copySalesTable = originalSalesTable;
  copySalesTable2.data(originalSalesTable);

  info (num2str(copySalesTable.RecId, -1, -1, -1, -1));
  info (num2str(copySalesTable2.RecId, -1, -1, -1, -1));

  // using .data() method to mimic change in datasource / deep copy
  originalSalesTable.data(SalesTable::find('01323_036'));

  // the rule I'm describing in this post does not work if I used the following:
  // uncomment the line below and comment the line above to test
  // originalSalesTable = SalesTable::find('01323_036');

  info (num2str(copySalesTable.RecId, -1, -1, -1, -1));
  info (num2str(copySalesTable2.RecId, -1, -1, -1, -1));
}


Copy and paste this last code snippet if you want to test this exception to the rule I'm trying to put forward :-)

If you any questions or comments e-mail me on: mirko@mirkobonello.com

Getting the number of decimal places for an EDT

Sometimes you might need to get the amount of decimal places set on the EDT. This can either be set to 'AUTO', where the decimal places set in the server's regional settings are taken or manually inputted by the developer in the property inspector.

The following is a code snippet of how this can be done, catering for both scenarios mentioned above:


#DEFINE.AUTO('Auto')

#AOT
#PROPERTIES
#WinAPI // Used for regional settings

TreeNode treeNode;
Int decimalPlaces;
;
treeNode = infolog.findNode(#ExtendedDataTypesPath + '\\' + identifierstr(YourEDT));

if (findproperty(treeNode.AOTgetProperties(),#PropertyNoOfDecimals) == #AUTO)
{
  // get the number of decimals from the regional settings
  decimalPlaces = str2int((WinAPI::getLocaleInfo(#LOCALE_USER_DEFAULT,   #LOCALE_ICURRDIGITS)));
}
else
{
  // get the number of decimals set by the developer in the property inspector
  decimalPlaces = str2int(findproperty(treeNode.AOTgetProperties(),#PropertyNoOfDecimals));
}

info (int2str(decimalPlaces));


If you any questions or comments e-mail me on: mirko@mirkobonello.com

Friday, January 09, 2009

Getting the number of records from table name.

I replied to the following question on the forums:

How to check if a table has records in it, from its name, NOT from a buffer.

The trick is some standard AX dict classes reflection (specifically DictTable):


static void recordsExist(Args _args)
{
  SysDictTable dictTable;
  ;
  dictTable = new SysDictTable(tableName2Id('TableStringHere'));

  if (dictTable)
  {
    if (dictTable.recordCount() > 0)
      info ("This table has records.");
    else
      info ("No records");
  }
  else
    info ("Table does not exist");
}


If you any questions or comments e-mail me on: mirko@mirkobonello.com

Thursday, January 08, 2009

3rd Party Document Integration Tool

Bottom Line is a company whose website I found while I was browsing the web.

According to the site it works on both Dynamics Ax and Dynamics NAV and provides powerful document integration with a nice report designer, and a cool document integration workflow designer and other features like barcodes, cheques, integration to e-mail, fax, bank transfers etc.

I haven't tried the product but viewed the 6 minute presentation on the website. Hope to see it working in action some time.

A video about this can be found on Channel 9 right here .

Note: I am not affiliated to this company in any way.

If you any questions or comments e-mail me on: mirko@mirkobonello.com

Fixed field and Related fixed field

This is an answer I gave to a question on a forum regarding fixed field and related fixed field:

Lets say you have ClothesTable and ClothesOrders.

ClothesTable has the following fields: ClotheId, Name and CollectionTypeId

MenClothesOrder has the following fields: OrderId, ClotheId, Qty OrderId could be a number sequence and Qty entered manually by the user.

CollectionTypeId has the following elements:

0 - Men

1 - Women

2 - Children

Example 1: Related Fixed Field

On MenClothesOrder we create a new relation to ClothesTable and specify the follwing two:

1. Normal = ClotheId to ClotheId (Best practice to specify this on the EDT but you have to do it here on the table too for this to work) and

2. Related Fixed Field 0 = ClothesTable.CollecTionTypeId.

This shows that the lookup to the clothes table should show only clothes with the same ClotheId (point 1) AND clothes that are of type Men (point 2) because the our table deals with order for mens' clothes. We use 0 because Menis element 0 in the Enum.

Example 2: Fixed Field

This kinda works the other way round:

Imagine you have a ClothesOrders table (generic) and you have seperate tables for MenClothesTable, WomenClothesTable and ChildrenClothesTable. Fixed field says that the specified normal relation (on ClotheId) to MenClothesTable only works if the CollectionTypeId of the current record is set to 0 (Men) else the relation is disabled.

If you need furhter clarification do not hesitate to ask.

If you any questions or comments e-mail me on: mirko@mirkobonello.com