Wednesday, September 18, 2013

Caching display methods with Client modifier

Caching display methods can increase performance in some cases. But be cautious when you are adding them in datasource Init method. If by mistake you add one method that defined with Client modifier on the table, it will fail right calculations for them all for the first line in the grid.

For example, you have one method on the table with Client modifier:

display client Integer isLineChanged()

    if (this.Changed)
        return #ImageInfo;

    return 0;

On the form you add this method amongst others in cache (not the last one!):

// Form DataSource Init method
public void init()
    this.cacheAddMethod(tablemethodstr(myTable, errorExist));
    this.cacheAddMethod(tablemethodstr(myTable, isLineChanged));
    this.cacheAddMethod(tablemethodstr(myTable, itemName));
    this.cacheAddMethod(tablemethodstr(myTable, categoryName));
    this.cacheAddMethod(tablemethodstr(myTable, subCategoryName));

You will get the following result because all of these methods will be calculated based on client side buffer that is not fetched yet.

It does not matter if you place a cached display method in the form or not -- it is calculated anyway.

Friday, September 13, 2013

How to compare two records of the same table

Amongst other mundane chores, from time to time AX consultants and programmers need to check if two table records are indentical and what the difference is if they are not.

Here is a small project for AX 2012 with tmxTableBufferOperation class that provides the following static methods: 
  • getOneRecordFieldList(Common _record1) 
  • getTwoRecordsFieldList(Common _record1, Common _record2) 
  • areIdentical(Common _record1, Common _record2)
  • getDifference(Common _record1, Common _record2)
Their names are mnemonical so you won't misunderstand what they do.

There is also a batch demonstrating how to use them:

The main idea is to use reflection DictTable class to enumerate all the fields of the given table buffer and then populate containers with its field ids, names and values.

static public List getTwoRecordsFieldList(Common _record1, Common _record2)
    Common              buffer1;
    Common              buffer2;

    List                list        = new List(Types::Container);   //list of all the fields, field names and values
    List                bufferList  = new List(Types::Container);   //final list of all the fields, field names and values of these two records
    ListEnumerator      le;

    int                 i;
    fieldId             fieldId;
    DictTable           dictTable;
    DictField           dictField;

    container           c;

    if(_record1.TableId != _record2.TableId)
        error(strFmt('Both records are supposed to be of the same table type!'));

    dictTable = new DictTable(_record1.TableId);

    if (dictTable)
        // create the list of all the fields in the table
        for (i = dictTable.fieldCnt(); i; i--)
            fieldId = dictTable.fieldCnt2Id(i);
            dictField = new DictField(, fieldId);
            list.addEnd([fieldId, fieldId2name(, fieldId)]);

        buffer1 = dictTable.makeRecord();
        buffer2 = dictTable.makeRecord();

        select buffer1 where buffer1.RecId == _record1.recId;
        select buffer2 where buffer2.RecId == _record2.recId;

        le = list.getEnumerator();
        while (le.moveNext())
            c = le.current();
            bufferList.addEnd([[conPeek(c,1), conPeek(c,2), buffer1.(conPeek(c,1))],
                               [conPeek(c,1), conPeek(c,2), buffer2.(conPeek(c,1))]]);
    return bufferList;

And how you can use it.

static void tmxCompareFixedAssets(Args _args)
    List                    list;
    ListEnumerator          le;
    container               c;
    list = TmxTableBufferOperation::getDifference(AssetTable::find(#FA1), AssetTable::find(#FA2));
    le      = list.getEnumerator();
    info('Difference 1 <> 2');
    while (le.moveNext())
        c = le.current();
        info(strFmt("%1: %2 <> %3", conPeek(conPeek(c,1), 2), conPeek(conPeek(c,1), 3),  conPeek(conPeek(c,2), 3)));