Saturday, 25 April 2015

AX2012 R3 : Scheduling dates using X++

Hi Friends,
When required to calculate a future/past dates using X++  based on the working times defined in the working calendar in AX, workcalendarsched class can be used to take off the pain.
The below job shows how we can use the schedDate function and get the result, for simplicity I have passed the calendar name defined in my system as a string:



TransDate       reqDate = systemdateget(), newReqDate;
workCalendarSched   workCalendarSched = new workCalendarSched();
   
newReqDate = workCalendarSched.schedDate(SchedDirection::Forward,reqDate,5,false,'STANDARD');
    info(strfmt("%1",newReqDate));

Another useful method in this class is isdateopen which can be used to check if a particular date is open in the calendar. The good thing about this class is that it uses SysGlobalObjectCache to store and retrieve the values. It improves the performance when running long and complex calculations.

Notice in the below method, if the key exists in cache then the value is returned from there itself else the cache is updated.



The below method of WorkCalendarSched class initialises the variables from global cache.

The global cache scope is stored on the combination of Class + current partition and current company as highlighted below:



This class is also a good example to understand how to use SysGlobalObjectCache.
Thanks for reading the blog. Keep sharing.

Friday, 10 April 2015

Importance of SaveLast() in RunBase Framework

Hi Friends,
RunBase Framework is widely used in AX. In this post, I would like to highlight the importance of SaveLast() function. Understanding of pack/unpack pattern is must to utilize this class in the most effective way.

SaveLast() is used to save the latest state of the class. That means even after the class is unpacked we can still update the packed state of the class.  



One common scenario where it is very useful is when we need to show the values of fields from the caller record on the dialog . In such scenarios this method needs to be called before the prompt() method . This way the packed state is updated with the latest values and in the dialog() method you can access the latest values. 

This can been seen in many standard AX classes which extend RunBase class





So if you are packing some variables, which you are also initialising in the main method and want to show on the dialog, then call saveLast() method before prompt() to get the most recent values.

Thanks for reading the blog. Have a great day.

Saturday, 21 February 2015

Generating QR Codes in AX 2009

Hi Friends,
Recently I worked on a task to generate and show QR code on a report in AX 2009.

In order to do this, I did the below steps:
  • Downloaded file MessagingToolkit.QRCode.dll
  • Copied it in client\bin folder

  • Added this dll file in the system references


  • Create a new display method on the report. The below code can be used to generate QR code.
display container qrcode()
{
    Bindata                 bindata = new Bindata();
    Image                   Imgobj;
    System.Drawing.Image    img;
    System.Drawing.Bitmap   obj;
    Filepath                _path;
    container               con;
    MessagingToolkit.QRCode.Codec.QRCodeEncoder encoder = new MessagingToolkit.QRCode.Codec.QRCodeEncoder();
    ;
    obj = new System.Drawing.Bitmap(encoder.Encode("TESTSTRING"));
    obj.Save(@"C:\temp\newPhoto.bmp",System.Drawing.Imaging.ImageFormat::get_Bmp());
    bindata.loadFile(@"C:\temp\newPhoto.bmp");
    con = bindata.getData();
    return con;
}


  • Add the image control on the report based on the display method. 



QR code can be seen on the report on running it.



There are many other dll files available as open source to generate QR code. Keep sharing.

Reference: I found a link to do the same on AX2012

Wednesday, 14 January 2015

AX2012 R3 : X++ code to start production order

Hi Friends,
The below X++ code can be used to start production orders in AX2012 R3 and R2 versions. I have not tested this in previous versions.





The values which we normally define on start screen can be assigned in the prodParmStartUp table buffer:


In the job I have set the automatic BOM and Route consumption as Never and have also turned off the post picking list and route card journal check boxes.

To use the code quickly here it is, the production order number and quantity are initialized in the _prodId and _qty variables:

static void prodStart(Args _args)
{
    ProdParmStartUp             prodParmStartUp;
    ProdMultiStartUp            prodMultiStartUp;
    ProdId                              _prodId = "W000034";
    Qty                                   _qty = 10000;

    ttsbegin;
    prodMultiStartUp = ProdMultiStartUp::construct(null);
    prodMultiStartUp.parmId(RunBaseMultiParm::getSysParmId());
    prodMultiStartUp.getLast();
    prodMultiStartUp.insert(ProdTable::find(_prodId), prodParmStartUp);
    if (_qty)
    {
        prodParmStartUp.StartUpQty                     = _qty;
        prodParmStartUp.PostNowBOM               = NoYes::No;
        prodParmStartUp.PostNowRoute              = NoYes::No;
        prodParmStartUp.BOMAutoConsump      = BOMAutoConsump::Never;
        prodParmStartUp.RouteAutoConsump      = RouteAutoConsump::Never;
        prodParmStartUp.update();
    }
    prodMultiStartUp.runOnServer();

    info("Done");
    ttsCommit;
}

Thanks for reading the blog. 

Saturday, 8 November 2014

X++ code to change financial dimension when assigning Inventory Site

Hi Friends,
It is good to find the out of the box available API's in Microsoft Dynamics AX, which can take the burden of writing some tricky code off your head. I recently found this API which can be used to link an existing financial dimension with the financial dimension defined on an Inventory site.

This is useful in scenarios where we create journals using X++ code and default/assign the value in inventory site field. This is a static method names changeDimension() available on InventSite table which takes the current financial dimension and the InventSiteID as inputs and returns you the merged Financial dimension. \Data Dictionary\Tables\InventSite\Methods\changeDimension()

A sample X++ code when creating an Inventory journal lines, to use this API would be something like this :
inventJournalTrans.DefaultDimension = InventSite::changeDimension(inventJournalTrans.DefaultDimension,'SITE001');

Inside this method you will find that system uses DimensionDefaultingService class to merge the dimensions.  

You will also notice in the comments that the dimension is merged only when the site link is enabled.
This means that if the financial dimension is linked to Site and the dimension link is locked then any transaction against this site can only be posted if the value of the selected dimension matches the value associated with the Site storage dimension. This setup form can be found at Inventory and Warehouse management -> Setup -> Posting -> Dimension link.
.

 
 
So if you are writing X++ code to assign inventory site to any table record which is also storing financial dimension then use the above API . This will make sure that the financial dimension value is merged properly during the creation of the record.

Thanks for reading the blog, Keep sharing.
 

Saturday, 6 September 2014

Read properties of table fields using X++ code

Hi Friends,
The below piece of X++ code can be used to read properties of table fields.
To demonstrate, the below job will check the mandatory property of the fields of a table. This comes handy during technical analysis. It can be time consuming to check the property of each field manually.

The code uses DictTable and DictField classes to iterate through the table fields and it's properties. This can also be used to check other properties of the fields of a table.


static void checkProperties(Args _args)
{
    DictTable       dictTable;
    DictField       dictField;
    int             i, cnt;
   
    dictTable = new DictTable(tableNum(CustTable));
    cnt = dictTable.fieldCnt();
    for (i= 1; i<=cnt;i++)
    {
        dictField = new DictField(tableNum(CustTable),dictTable.fieldCnt2Id(i));
        if (dictField.mandatory())
        {
            info (strFmt("Field %1 is mandatory.",dictField.label()));
        }
    }
     
}
Similar to the way we have checked the property of field using dictField object in the above job, we can also read properties of any table using the dictTable object.

These classes are used at lot of places in standard AX code to iterate through table structures. Some common scenario are when we need to show list of table/fields in lookups. 

Sunday, 10 August 2014

Using NOT LIKE in X++ select statements and query ranges

Hi Friends,
In this post I'll like to share how to use NOT LIKE operator in X++.
LIKE keyword is available in X++ but  NOT LIKE is not directly supported the way it works in SQL. It still can be achieved using X++ select statements as well as in query range values.

For illustration, I'll use an example to select all customer group records which do not start with "1" and do not start with "2", using a select statement as well as using a query object in X++.

Query Object:
If we need to write this in query range, then you need to use the power of expressions in the query range value. Visit using expressions in query ranges to get more details.
In the expression use '!1*' to define a not like condition and then separate other values  by a comma (,) as a comma will represent an AND condition in the query range expression. 


To quickly use the code, here it goes:

    CustGroup               custGroup;
    Query                   query;
    QueryBuildDataSource    qbds;
    QueryBuildRange         qbr;
    QueryRun                qr;

    query = new Query();
    qbds  = query.addDataSource(tableNum(CustGroup));
    qbr = qbds.addRange(fieldNum(custGroup,CustGroup));
    qbr.value(strFmt("%1,%2",strFmt('!%1*','1'),strFmt('!%1*','2')));

    info (qbds.toString());
    qr = new QueryRun(query);
    while (qr.next())
    {
        custGroup = qr.get(tableNum(CustGroup));
        info (custGroup.CustGroup);

    }


Select Statement:
If we need to write in select statement, then you can achieve by enclosing the like condition in brackets and then use a ! operator (! is the NOT operator in X++). Refer Keywords in X++ for details.

So if there are multiple NOT LIKE conditions which needs to be applied in a single statement, then each condition needs to be enclosed in brackets and need to have a ! operator applied as shown below.

You can see that the customer groups table has three records which starts with 1 and 2 but they are not shown in the info log.

To quickly reuse the code here it goes:

 CustGroup CustGroup;
    ;
    while select CustGroup
        where (!(CustGroup.CustGroup like '1*') && !(CustGroup.CustGroup like '2*'))
    {
        info(CustGroup.custGroup);

    }


The above code works well with AX2012 R3/R2/RTM and 2009 versions. I have not tested in any previous versions. The above information as also be found at MSDN  X++, ANSI SQL Comparison: SQL Select

Thanks for reading the blog and keep sharing.