Saturday, September 13, 2014

How to add a new relation on a table from code

If you got into troubles, like me, trying to add a new relation on a table from X++, this code will save your day. The main point here is the code 251. Read my lips: two-five-one. Because reflections work on everything when you know the code.



private void createTableRelationInAOT(TreeNode _treeNodeTableRelation, TableName _relatedTableName, FieldName _relatedFieldName)
{
#Properties    
    TreeNode            treeNodeRelations;
    TreeNode            tableRelation;
    ReferenceNode       tableRelationFields;

    if(treeNodeTableRelation && _relatedTableName && _relatedFieldName)
    {
        treeNodeRelations = _treeNodeTableRelation.AOTfindChild(#PropertyRelations);
        tableRelation = treeNodeRelations.AOTadd(relatedTableName);
        tableRelation.AOTsetProperty('Table', relatedTableName);
        //can anyone tell me how I could guess this secret code??
        tableRelationFields = tableRelation.AOTaddSubNode(251); 
        //never change the order of two these properties! it works only like follows
        tableRelationFields.AOTsetProperty(#PropertySecField, relatedFieldName);
        tableRelationFields.AOTsetProperty(#PropertyRelatedField, relatedFieldName);
    }
    _treeNodeTableRelation.AOTsave();

}


Happy deciphering!

Sasha Nazarov added:

 "251" corresponds to #NT_DBNORMALREFERENCEFIELD

in TreeNodeSysNodeType macro:
#define.NT_DBREFERENCE( 250)
#define.NT_DBNORMALREFERENCEFIELD( 251)
#define.NT_DBTHISFIXEDREFERENCEFIELD( 252) #define.NT_DBEXTERNFIXEDREFERENCEFIELD( 253)

Thursday, September 11, 2014

Problem with importing a temporary table/class/etc

I bumped into a strange problem. The admin refreshed my development environment from the TEST one; consequently, all my objects were deleted.

When I started re-importing them from an exported xpo-project, a temporary table always caused the following error:

A table, Extended Data Type, Base Enum or class called XXXXX already exists. Import of Table aborted.


The problem can be easily resolved by flushing the user cache:
Stop AX client and delete all  ax_*.auc files in "C:\Users\%USERNAME%\AppData\Local folder" 

Monday, August 25, 2014

How to fix internet speed drop via Wi-Fi connection (Cisco router)

Having read a lot of tricks and tweaks on how to fix speed drop, I found a solution in my Cisco Wi-Fi router QoS settings.


Friday, August 15, 2014

Display methods and Caching on forms

Even though the form controls using display methods are deleted from the form, they are still executed if added in cache in Form datasource Init() method.

Wednesday, June 18, 2014

How to count query loops number

Amongst our other mundane chores, counting of a query loops is not the trickiest one but, unfortunately, still clumsy developed even in AX 2012.

 Not only is it strange to have two different methods for one only and multiple data sources, but it takes long time and returns incorrect results when it comes to groupings.

 SysQuery::countTotal and SysQuery::countLoops use the private method SysQuery::countPrim, which can be redone in a way suggested here (in Russian).

private server static container сountPrim(container _queryPack)
{
    Query                   countQuery;
    QueryRun                countQueryRun;
    QueryBuildDataSource    qbds;
    QueryBuildFieldList     qbfl;
    Common                  common;
    Integer                 counter;
    Integer                 loops;
    
    Integer                 tmxGroupNumber;
    Integer                 tmxDataSourceNumber;
    ;
    countQueryRun   = new QueryRun(_queryPack);
    countQuery      = countQueryRun.query();
    tmxGroupNumber  = countQuery.groupByFieldCount(); //<-- this guarantees number of groupings

    for (tmxDataSourceNumber = 1; tmxDataSourceNumber <= countQuery.dataSourceCount(); tmxDataSourceNumber++)
    {
        qbds = countQuery.dataSourceNo(tmxDataSourceNumber);
         qbds.update(false);
        //qbds.sortClear();

        //tmxGroupNumber +=(qbds.orderMode()==orderMode::GroupBy); 
        qbfl = qbds.fields();
        qbfl.dynamic(false);
        qbfl.clearFieldList();
         qbds.addSelectionField(fieldNum(Common,RecId),SelectionField::Count);
    }
  
    countQueryRun   = new QueryRun(countQuery);

    while (countQueryRun.next())
    {
         common  = countQueryRun.getNo(1);
        counter += common.RecId;
        loops++;
    }
    //return [counter,loops];
    return [counter,(tmxGroupNumber ? loops : counter)];
 }

Thursday, May 29, 2014

How to update cross-reference in batch

As you can see in AX 2012 updating cross references is not a batch job any more. But is worth setting it up to be run automatically during the night.

There is the article on MSDN describing this subject but without a batch, and if it looks too complicated for you, as for me, I would suggest another way found on AXForum (in Russian).

Once you started cross-reference updating from the client, you can easily find this job in Batch Job menu.





As we can see it is xRefUpdateIL class used for this goal, that in turn runs UpdateAll method.




So, it is possible to create your own job or batchable class to run, but we can just change the recurrence and set up other parameters, like alerts, for the ended batch job and change then its status to Waiting.



The same is true for partial reference update, say, for certain tables and classes of a project.



Happy cross-reference updating!


Thursday, May 1, 2014

Clean off the illegal characters from file name

When it comes to use file names, say, in saving reports on the disk or sending them as an attachment, they must conform OS restrictions.

Although we cannot rely on GetInvalidFileNameChars function due to its limitation, it is easy to create your own procedure that clean off the illegal characters from the given file name.

In my example I use a regular expression and a set of characters that should be replaced with the underscore by default.


/// <summary>
/// Checks for invalid characters in the filename and changes them with underscore.
/// </summary>
/// <param name="_fileName">
/// The file name value.
/// </param>
/// <param name="_char2use">
/// The character to use instead of illegal characters. Underscore by default.
/// </param>
/// <returns>
/// valid file name
/// </returns>

static client str makeFileNameValid(str _fileName, str 1 _char2use = "_")
{
    str                 pattern = "[/:*?'<>|]";
    str                 fileName;

    fileName = System.Text.RegularExpressions.Regex::Replace(_fileName, pattern, _char2use);
    fileName = strReplace(fileName , '\\', _char2use);
    return fileName;
}

I used this article.