Thursday, December 31, 2015

AX 2012 - RunBaseBatch Multithreading

Hi All

In AX 2012 Microsoft introduced the new framework (Business Operation Framework service) in order to consume a Batch process especially in Multithread.

But, you can use also the "old" RunBaseBatch class.
About it, take a look to this very interesting POST.

In order to submit a "task" class you can use two methods :

1- "AddRuntimeTask" method.
Adds an instance of a dynamic BatchTask to the BatchHeader.  The BatchTask added exists for only the current run.  It gets copied into the history tables and deleted at the end of the run.  The second parameter, inheritFromTaskId, copies settings such as the batch group and child dependencies from the task specified.

2- "AddTask" method.
Adds an instance of a BatchTask to the BatchHeader.  The BatchTask represents a batch-enabled class that will be scheduled for execution as a task by the BatchHeader.

So, you have to use the first one method otherwise if the process is recursive also the previous child completed tasks will be moved in running state together with the new tasks.

Happy new year!!

 

Tuesday, December 29, 2015

AX 2012 – Export Model Error: The file exists

Hi Guys

Never seen this error? If not, you are lucky!
I faced it during the TFS Build Process. The service is associate to the USER 1.

The strange thing is that the same command run with the User (USER1) works fine. The issue coming running the command in the USER 2 session (RDP Session) and impersonate USER 1.
I didn't understand the reason, so the workaround is been to create a new Active Directory user and associate it to the TFS Build Service.

That's it!

26/06/2017 : I have understood the issue! We need to delete the content of the Temp TFS agent user folder, like C:\Users\TfsAgent\AppData\Local\Temp

AX 2012 - AOS Freeze or Crash during CIL Compilation


Hi All

Today I’d like to share an issue that took a lot of my time!
I'm using AX 2012 R3.
At a certain point, the CIL Compilation failed and the AOS crashed or freeze without any errors that could help me.

For my lucky I use TFS so I try to verify the last check-in, one by one.


Finally I found the problem.

The code below cause the AOS Crash or Freeze during CIL Compilation, Incremental and Full as well.


public void updateRecordsetList()

{

    CustInvoiceTable    custInvoiceTable;

    CustinvoiceJour     CustinvoiceJour;

    CustInvoiceLine     CustInvoiceLine;


    update_recordSet CustinvoiceJour

        setting InvoiceAmount = custInvoiceTable.TotalAmount()

        join *

        from custInvoiceTable

        where custInvoiceTable.InvoiceId == CustinvoiceJour.InvoiceId

        join parentRecId, TaxAmount, AmountCur

        from custInvoiceLine

        where CustInvoiceLine.ParentRecId == custInvoiceTable.RecId;

}
 

The “TotalAmount” custom method of the CustInvoiceTable table contain the following query:


Display amount TotalAmount()

{

    CustInvoiceLine     custInvoiceLine;

   

    select sum(AmountCur) from custInvoiceLine

        where custInvoiceLine.ParentRecId == this.RecId;

 

    return custInvoiceLine.AmountCur;

}

I know that the code isn’t properly written, but, I expect an error.

This issue coming after the last Kernel update.

Before, with AX 2012 RTM Kernel, the CIL Compilation raise an error like below.

Error 1. Class: XXXXX, Method: updateRecordsetList, Exception: System.NullReferenceException: Object reference not set to an instance of an object.

   at Microsoft.Dynamics.Ax.Xpp.ILGenerator.processBytecode(XmlNode childNode)

   at Microsoft.Dynamics.Ax.Xpp.ILGenerator.processBytecodes(TypeBuilder typeBuilder, XmlNode node)

   at Microsoft.Dynamics.Ax.Xpp.ILGenerator.GenerateIL(TypeBuilder typeBuilder, XmlNode node)

   at Microsoft.Dynamics.Ax.Xpp.ILGenerator.TraversePCode(XmlNode node)

   at Microsoft.Dynamics.Ax.Xpp.PCodeTraverser.TraverseMethod(XmlNode methodNode)

   at Microsoft.Dynamics.Ax.Xpp.ILGenerator.GenerateXppModule()


So, with the last Kernel Microsoft introduce something…



I raised a Support Case and Microsoft suggestion is been modify the BP check (around line 222) in the “checkSource” method of the “SysBPCheckMemberFunction” class:


        // Check if setting value from valid function in update_recordset statement

        if ((textBuffer.find('update_recordSet'))

         && (textBuffer.find('setting',textBuffer.matchPos()+textBuffer.matchLen()))

         && (textBuffer.find('=',textBuffer.matchPos()+textBuffer.matchLen()))

         && (textBuffer.find('(+)',textBuffer.matchPos()+textBuffer.matchLen()))

           )

        {

            setMatchPosLineAndColumn();

            if (!isComment())

            {

                this.addSuppressableWarning(#BPErrorMethodUpdateRecordSetCheck, matchPosLine, matchPosColumn, strFmt("Are you setting a value from a supported method in update_recordset?"));

            }

        }

With this modification at least the Developer will not be able to compile the object because will raise a BP error.

 
From my point of view, this issue is time consuming because could take a lot of time.

I hope that Microsoft will improve the Compilation process in order to avoid an AOS crash.
I also create a suggestion in Connect, https://connect.microsoft.com/dynamicssuggestions/feedback/details/2182000

Stay Tuned!

Wednesday, December 23, 2015

TFS – Add or Duplicate a Work Item Type


Hi Guys

In order to add a new Work Item Type you can take a look at https://msdn.microsoft.com/en-us/library/hh409273.aspx

A more easy and fast way is to use the Microsoft Visual Studio Team Foundation Server Power Tools.

For instance, if you have TFS 2013 you can find the tool at the follow link.

Practically, in order to duplicate or create a Work Item Type the steps are:

1-      Install the Tool according the TFS version

2-      Open Visual Studio and go through the menu Tools\Process Editor\Work Item Types\Export WIT

3-      Choose the proper Team Project

4-      Choose the origin Work Item Type

5-      Choose the destination folder

6-      Edit the XML ( i.e. Notepad )

7-      Rename the WORKITEMTYPE and DESCRIPTION Tag

8-      Save the XML

9-     Come back to Visual Studio

10-   Go through the menu Tools\Process Editor\Work Item Types\Import WIT

11-   Choose the XML file

12-   Choose the Team Project

13-   Press OK

That’s it!
 
 

Sunday, December 20, 2015

AX 2012 - Use Paging feature in the AIF Document Service

Hi All
As you know, the Document Service doesn't support the Paging, only Query Service.
Take a look here
 
Below the trick to enable this feature also for the Document Service:
1-      Take a look to the standard class AxdPricelist, prepareForQuery method
2-      Practically, the service use a temporary table that it’s filled every time the service is called
3-      So, the idea is use a temp table and fill it with the proper data coming from a “direct” query to SQL
4-      The Temp Table must have two extra fields, like PageNumber and RowNumber
5-      So, in the prepareForQuery method execute a query directly to SQL using Paging SQL feature like :
a.       Select Field1, Field2, Fieldn from Table\View Order By Field Desc OFFSET ((' + PageNumber + ' - 1) *' + RowsPage + ') ROWS FETCH NEXT ' + RowsPage + ' ROWS ONLY
6-      From my side I used the Statement class and fill the temp table like :
    ResultSet = statement.executeQuery(SQL);
 
    while ( ResultSet.next() )
    {
        TempTable.clear();
 
        TempTable.Field1                = ResultSet.getXX(1);
        TempTable.Field2                = ResultSet.getXX(2);
        …..
 
        TempTable.PageNumber  = PageNumber;
        TempTable.RowsPage    = RowsPage;
 
        TempTable.insert();
    }
 
7-   Before to execute the query you have to retrieve the “range” fields, so PageNumber and RowNumber like :
    Qbr1 = _query.dataSourceNo(1).findRange( fieldNum(TempTable, PageNumber ) );
    Qbr2 = _query.dataSourceNo(1).findRange( fieldNum(TempTable, RowsPage   ) );
 
8-      In a complex queries, we have to create an AX View and use it in the above query.
9-      I have used the OFFSET clause. This feature is available for the SQL Server 2012 and above version. Otherwise you have to use the ROW_NUMBER clause.
Stay Tuned!

Monday, December 7, 2015

AX 2012 Close Open Forms


Hi Guys

About this topics I spent some time and at the end I’m not able to call by code the standard AX 2012 command in order to Close All Forms, see picture below.



 
In order to achieve this goal, I have modified the SysSetupFormRun Class, Run Method and saved the Window handle of all open forms in a List and move it in the Global Cache, like below :

    // Exclude ListPage Forms
    If (this.form().formTemplate() != 1

       )

    {
                …..

classFactory.globalCache().set(...

 

Finally, using the sendMessage WinApi Function I have closed all Forms, like below :

    #WINAPI

    ……
    listIterator ....
    WinApi::sendMessageEx( WindowHandle, #WM_CLOSE, 0, 0);
 
 
 
 
That's it!

Stay Tuned!

Friday, October 30, 2015

AX 2012 - How to know if an AX User has the "Execute business operations in CIL" option enable ?

Hi All

Seems an easy question, but today I spent some time to get this answer.

The answer is :

    If ( appl.parmExecuteBusinessOperationsWithCLR() )
        info ("Enable");
    else
        info ("Disable");

Also, take a look to this link https://msdn.microsoft.com/en-us/library/hh528509.aspx

Stay Tuned!
 

Thursday, October 29, 2015

Retrieve the Query name linked to a View

Hi All

Below the code in order to retrieve the query name that defines a View.
    #AOT
    #Properties

    TreeNode          treeNode;
    QueryName         QueryName;

    treeNode = TreeNode::findNode(#ViewsPath+'\\'+ 'ViewName');
    QueryName = findProperty(treeNode.AOTgetProperties(), #PropertyQuery);
   
    info ( QueryName );

That’s it!
 

Wednesday, September 16, 2015

AX 2012 - How to intercept the AX Shutdown?

Hi All

How to block the AX Shutdown?

Easy, go through the Info class, canShutdown method.
So, insert an IF condition before the Return and ask to the user if is sure to close AX.

That's it!

Monday, September 7, 2015

AX 2012 – Have a list of the SSRS Reports to a specific Layer using Model tables



Hi Guys


Below a quick code to have the list of SSRS Reports created or modified to a specific Layer or Model.


Run this against the Model DB

By Layer will be :


use ModelDb


SELECT a.name, a.origin, a.axid, a.parentid, b.modelid, c.LayerId, d.name, e.name


  FROM ModelElement a


  join ModelElementData b


  on a.ElementHandle = b.ElementHandle


  join model c


  on b.ModelId = c.id


  join layer d


  on d.Id = c.LayerId


  join ModelManifest e


  on e.ModelId = b.modelid


  where a.elementType = 85


  and   c.LayerId <> 0


  and   d.name = 'VAR'





By Model will be :



use ModelDb


SELECT a.name, a.origin, a.axid, a.parentid, b.modelid, c.LayerId, d.name, e.name


  FROM ModelElement a


  join ModelElementData b


  on a.ElementHandle = b.ElementHandle


  join model c


  on b.ModelId = c.id


  join layer d


  on d.Id = c.LayerId


  join ModelManifest e


  on e.ModelId = b.modelid


  where a.elementType = 85


  and   c.LayerId <> 0


  and   e.name = 'ModelName'



Enjoy!

Wednesday, July 22, 2015

TFS - How to view and export the Changeset ?

Hi All
 
Below an easy way to view and export the TFS Changeset.
 
1.      Open the Source Control Explorer

2.      Go through "View History" from your Team Project

3.      Go to the bottom of the list

4.      Select all records

5.      Copy all

6.      Open Excel and paste the selection
Now you are able to analyze the data using the power of Excel.
 
That's it!
Stay tuned!

Sunday, July 19, 2015

Microsoft Dynamics AX 7 Launch Portal

Hi All


Microsoft have published the Launch Site for Dynamics AX 7 with WPC Sessions, Launch Events, Sales & Marketing, etc.


Microsoft Dynamics ‘AX 7’ Launch Portal


Stay Tuned!

Sunday, April 19, 2015

AX 2012 - How to fix Data Dictionary ( SqlDictionary ) issue after importing a Model, ModelStore or during the TFS BUILD Process

Hi All
 
During the development phase, many tasks are made on the Data Dictionary.
Create tables and fields, rename or delete them.
So, often the Synchronization process failed.
To avoid this issue, you can create a process that fix the SqlDictionary Table.
During the time, I created many Jobs.
 
The main started from the Martin post with the following changes :
1.       To avoid the duplicate key, I read the fields starting from the last one.
2.       For each table, I delete first the fields from the SqlDictionary that no more exist on AOT
 
If you use the TFS Build, before the Synchronization you can run the above job.
The same during the Modelstore transportation scripts.
Here the code :

    Dictionary dictionary = new Dictionary();
    SysDictTable dictTable;
    DictField dictField;
    TableId tableId;
    FieldId fieldId;
    Counter fieldCnt;
    SqlDictionary sqlDictionaryTable;
    SqlDictionary sqlDictionaryField;
    setPrefix("Update of data dictionary IDs");
    tableId = dictionary.tableNext(0);
    ttsbegin;
    while (tableId)
    {
        dictTable = new SysDictTable(tableId);
        setPrefix(dictTable.name());
        if ( dictTable.isSystemTable()
        ||   dictTable.isView()
        ||   dicttable.isTempDb()
        ||   dictTable.isTmp()
           )
        {
            tableId = dictionary.tableNext(tableId);
            continue;
        }
        ///////////
        //Finds table in SqlDictionary by name in AOT, if ID was changed.
        //Empty field ID represents a table.
        select sqlDictionaryTable
            where sqlDictionaryTable.name == dictTable.name()
            && sqlDictionaryTable.fieldId == 0
            && sqlDictionaryTable.tabId != dictTable.id();
        if (sqlDictionaryTable)
        {
            //Updates table ID in SqlDictionary
            if (ReleaseUpdateDB::changeTableId(
                sqlDictionaryTable.tabId,
                dictTable.id(),
                dictTable.name()))
            {
                info(strFmt("Table ID changed (%1 -> %2)", sqlDictionaryTable.tabId, dictTable.id()));
            }
        }
        ////////////
        // Delete Fields that no more exists in AOT
        while select forUpdate sqlDictionaryTable
            where sqlDictionaryTable.tabId == dictTable.id()
            &&    sqlDictionaryTable.fieldId
        {
            If ( ! dictTable.fieldName2Id( sqlDictionaryTable.name ) )
            {
                sqlDictionaryTable.delete();
            }
        }
        ////////////
        //fieldId = dictTable.fieldNext(0);
        fieldCnt = dictTable.fieldCnt();
        fieldId = dictTable.fieldCnt2Id(fieldCnt);
        //For all fields in table
        while (fieldCnt)
        {
            dictField = dictTable.fieldObject(fieldId);
            if (dictField.isSql() && !dictField.isSystem())
            {
                //Finds fields in SqlDictionary by name and compares IDs
                select sqlDictionaryField
                    where sqlDictionaryField.tabId == dictTable.id()
                    && sqlDictionaryField.name == dictField.name()
                    && sqlDictionaryField.fieldId != 0
                    && sqlDictionaryField.fieldId != dictField.id();
                if (sqlDictionaryField)
                {
                    //Updates field ID in SqlDictionary
                    if (ReleaseUpdateDB::changeFieldId(
                        dictTable.id(),
                        sqlDictionaryField.fieldId,
                        dictField.id(),
                        dictTable.name(),
                        dictField.name()))
                    {
                        info(strFmt("Field %1 - ID changed (%2 -> %3)",
                            dictField.name(),
                            sqlDictionaryField.fieldId,
                            dictField.id()));
                    }
                }
            }
            //fieldId = dictTable.fieldNext(fieldId);
            fieldCnt--;
            fieldId = dictTable.fieldCnt2Id(fieldCnt);
        }
        tableId = dictionary.tableNext(tableId);
    }
    ttscommit;

Thursday, April 16, 2015

Change the TITLE of the AX Main Window

Hi


When you open Dynamics AX, the main windows is a form.
So, if you want to change the Title, the code is very easy :
WinAPI::setWindowText( infolog.hWnd(), "Magic AX")
If you want to manage automatically,is enough move this code to the startupPost method of the Info class.
If you want trigger this code for every workspace, you have to move the code on the workspaceWindowCreated method of the Info class, like this :
WinAPI::setWindowText( _hWnd, "Magic AX");
Thas'it!
Stay Tuned!


Sunday, April 12, 2015

Second MVP Award on Dynamics AX

Hi All

Is a pleasure announce the renewal to the Microsoft MVP on Dynamics AX!

Although very busy with my daily activities I am happy to share my knowledge with others in the world.
Become an MVP mean enter in a big family, get in touch with other fantastic guys also in other expertise and finally get in touch with Microsoft!
With Yammer, direct message, etc. I have the opportunity to reach the best people in the world that can help me in my daily activities.

Finally, a big thanks to the others MVP on Dynamics AX, always available.
You can find and see them to the follow LINK

Look through this Video to resume my reaction in Office... -))

Enjoy with Dynamics AX!

Saturday, February 28, 2015

AX 2012 - Debug doesn't work, breakpoint doesn't stop

Hi Guys


About this topic, you will find many post in google.

With my, I just want to underline a strange behaviour that I have faced.
Pratically, in my scenario, the Debugger opened ( with many "flash" ) but doesn't stop to any code.


So, after check the Microsoft links :
https://msdn.microsoft.com/en-us/library/aa569665.aspx
https://technet.microsoft.com/en-us/library/hh202098.aspx


without success, I realize that the problem was related to the User Account Control (UAC) enable!


So, I have open Dynamics AX with administrative privileges and all worked fine!
Obviously, you can disable the UAC.


See you soon!