Showing posts with label AX 2009. Show all posts
Showing posts with label AX 2009. Show all posts

Wednesday, March 27, 2019

Common mistake, when creating Purchase Orders from X++

I would like to warn my fellow Axapta/AX/D365FO bloggers, that blind copy-pasting from each other seems innocent, but it fact may result in spreading bugs.

E.g., the following piece of code is very popular in "How to create Purchase Order from X++" posts.

numberSeq = NumberSeq::newGetNumFromCode(purchParameters::numRefPurchaseOrderId().NumberSequence,true);

Purchtable.PurchId = numberSeq.num();

The problem is numRefPurchaseOrderId returns the number sequence for purchase order confirmation number. For purchase order number, numRefPurchId should have been used. Using the wrong number sequence may result in duplicate key issues.

Tuesday, November 29, 2016

Tuesday, October 27, 2015

When fields lists don't work as expected

Did you know that a field list in a single-record select-statement is not always beneficial in comparison to a select firstonly * from...  (or a static find-method)?

The thing is, when a where-clause matches a unique index (in AX 2009 – a primary index), the AOS may find the complete record in the cache and return it. And if this is the very first call, it will pull the whole record anyway, in order to be able to cache it. This may be proved by debugging.

So please use find-methods where possible, because they are easier to read and maintain. And remember: a while select with a nested find-method call may actually perform better than a join – thanks to caching. Therefore do measure performance to confirm that your select-statement is the most optimal one.

Friday, June 19, 2015

Looking for EDTs with broken table relations

The other day I found a couple of EDTs with broken table relations and wrote a script that found even more EDTs with the same problem.

Broken relations look like this:


static void findBrokenRelationsInEDTs(Args _args)
{
    #AOT
    #TreeNodeSysNodeType
 
    TreeNodeIterator iterator;
    TreeNode         edtTreeNode;
    TreeNode         relationsNode;
    TreeNode         relationNode;
    ;
 
    iterator = TreeNode::findNode(#ExtendedDataTypesPath).AOTiterator();
    if (iterator == null)
    {
        throw error("Cannot create tree node iterator");
    }
 
    edtTreeNode = iterator.next();
 
    while (edtTreeNode != null)
    {
        relationsNode = edtTreeNode.AOTfindChild('Relations');
 
        if (relationsNode.AOTchildNodeCount() == 0
         || edtTreeNode.AOTname() like "DEL_*")
        {
            edtTreeNode = iterator.next();
            continue;
        }
 
        relationNode = relationsNode.AOTfirstChild();
 
        while (relationNode != null)
        {
            if (relationNode.sysNodeType() == #NT_DBTYPENORMALREFERENCE
             && (relationNode.AOTgetProperty('Table') == ''
              || relationNode.AOTgetProperty('RelatedField') == ''))
            {
                error(edtTreeNode.AOTname());
                break;
            }
 
            relationNode = relationNode.AOTnextSibling();
        }
 
        edtTreeNode = iterator.next();
    }
}

Output:


Tuesday, June 2, 2015

Which query range should I select?!

Customer asked me to figure out which "Customer account" should be selected in a query. There were three of them on the Customer invoice journal table:


Apparently, there were a couple of fields that used the default label, and I found them in the following way.

Selected the first "Customer account" range:


... right-clicked in the field value and selected Record Info:


... in the dialog, clicked on the Script button:


... pasted the clipboard to Notepad:


The extended field ID was 65540.

Then, I selected another range:


... and repeated the process. The second field ID was 81625. Finally, I selected the third Customer account range and found that it was based on the field ID 81626.

So, the three field IDs for CustInvoiceJour table were found. But what were the field names?

static void printFieldNames(Args _args)
{
    print fieldId2Name(tableNum(CustInvoiceJour), fieldExt2Id(65540));
    print fieldId2Name(tableNum(CustInvoiceJour), fieldExt2Id(81625));
    print fieldId2Name(tableNum(CustInvoiceJour), fieldExt2Id(81626));
    pause;
}

And the output was:

Somebody did not follow the Best Practices for Table Fields:






Tuesday, March 10, 2015

AX 2009: Extra BP check for report designs

Sometimes new report designs are created by duplicating original ones. AX adds postfixes to cloned controls automatically to keep names unique, but should there be any references to report controls in the code (say, in executeSection methods), those must be fixed manually.

If this is not done, there will be no compilation errors, but the code in the new design will point to report controls located under the original design, which does not make sense at run time.

I have customized SysBPCheckReportDesign class to check if the code must be fixed in the new design. The customization is based on the standard cross-reference functionality. There was one issue with that: some report control names were cut in the xRefTmpReferences table, so xRefName EDT had to be extended. xRefTmpReferences table is not customized, but it is included to the project, as it should be re-compiled after the project is imported and data dictionary is synchronized.

You can find the XPO here.

And this is what the imported project and the new BP errors look like:




Friday, September 26, 2014

CGI Code Review Tool for Dynamics AX

I am happy to announce the first release of a code review tool developed by CGI Group Inc.
The tool has been installed and successfully used at multiple customer project, both AX 2009 and 2012.

It has been decided to share the tool with the rest of the community, so you are very welcome to get it at CodePlex.

Any feedback is more than welcome.

Wednesday, April 9, 2014

Sales invoices in batch: performance issue solved

Our customer experienced huge performance problems with posting sales invoices in batch.

Teh customer had multiple recurring batch jobs run in their headquarter company. Each batch job processed intercompany sales orders corresponding to a particular regional office. They put the jobs to different batch groups and allocated separate AOSes for those groups.

At some point, the code in production has changed, but the batch jobs have never been recreated. It looks like version of their parameters became outdated and therefore the ranges were ignored at run time, and that resulted in all jobs trying to post all invoices. After their AX admin recreated the recurring jobs, the issue has gone.

So next time you are incrementing CurrentVersion macro in a RunBaseBatch ancestor, consider handling the old version in the unpack-method, so only new parameters will be nil after unpacking.