AX 7. Azure Logic Apps, CDM and PowerApps. Part 2.

In previous blog post we built simple scenario where we created customer in AX from external web request using Azure Logic App.

This time I am going to extend Logic App that was created before to support Common Data Model (CDM). There are couple of APIs that we can use:


In this case, I want to create a new record in CDM every time when a customer is created in AX.

We can achieve this simply by adding new API to our App and mapping the fields. I used “Customer” entity from CDM, which comes out of the box with CDM database.


After request is sent and customer is created, we have two options to check it in CDM: view records using Excel add-in or build new PowerApp.

Option 1: Go to, find your entity and open it in Excel.

Excel Add-in supports all features that we like in AX 7, so you can create, update and delete records with it.


Option 2: Create from new app in PowerApps Studio for Windows or PowerApps Studio for Web.  I personally prefer desktop version, because it works faster for me.


From PowerApps studio you can create new App for CDM, CRM or even for AX. When you create new CDM App you need to specify connection to database and select entity, same as above we will select “Customers”.

After that studio will automatically generate an App for us.


By default, new App has 3 screens: Browse, Details and Edit.

We will modify the layout and delete non relevant fields. So instead of Fax, Phone and Address we will put Customer Id and Description on Browse screen and add contact information to Details screen.


That’s all, now we can run the App and see what we’ve got.



Nice and simple way to build new App to work with customers without even a line of code!

AX 7. Azure Logic Apps, CDM and PowerApps. Part 1.

We are all exited with Dynamics365 coming. As we know, it is based on PowerApps, Common Data Model and Flow. Finally, we can put all these features together with connector to AX online which is released recently (

Today, I will show how Azure Logic Apps can be used in integration scenarios. For example, we want to create a new customer in AX from an external web service. In previous versions it was non-trivial task, but now it could be done in couple of clicks!

First of all, we need to create a new Logic App. We can do this either from Visual Studio or directly from Azure portal.

This app will receive requests from external web services and to handle this we will add HTTP request trigger that accepts JSON.


To build JSON schema you can use one of dozens online services. Here is example from one of them:


As you can see, my request contains AccountNum, DataAreaId, CurrencyCode, CustomerGroupId, name and AddressCountryRegionId fields.

On the next step we will add Dynamics AX connector that uses values from JSON request to create new customer. After specifying connection to AX 7, you can select any OData entity from drop down list and it will automatically add all mandatory fields. Each filed you can map with value from JSON simply selecting them from a list.


That’s all, save it and we can start testing.

To send the request I used Postman, but in real life scenario it would be a web service.


Voila! New customer has been created in AX.


On Azure portal you can track execution status and debug into each step, where you can see all data that was sent.



What’s next? You can add a response to notify web service if record was successfully created, send email with confirmation or even SMS.

In my next post I’m going to look at CDM and PowerApps, stay tuned!

AX 7. URLUtility extension to create deep links.

In AX 7 we can use URL to open any form and it’s quite handy. Out of the box there is a feature called “Get link” that helps us to generate and share links. We can find it under Options -> Share -> Get Link.


However, it has a limitation – you cannot generate link to open a specific record (so called “deep links”).

We can generate deep links from code by using URLGenerator class (there is a wiki article with explanations ), but it requires quite a bit of code to write. So I did small extension to URLUtility class to simplify this process.

It adds two new methods: generateRecordUrlFromDataSource and generateRecordUrl.

  • generateRecordUrlFromDataSource accepts form data source and generates a link to the record based on current record primary index fields.
  • generateRecordUrl accepts all parameters that are required to open a form, like menu item name and type, form data source name and map with key field’s IDs and values.
using Microsoft.Dynamics.AX.Framework.Utilities;
using Microsoft.Dynamics.@Client.ServerForm.Contexts;

/// <summary>
/// The class <c>URLUtility_Extension</c> contains extension methods for the <c>URLUtility</c> class.
/// </summary>
public static class URLUtility_Extension
    public static str generateRecordUrl(str _menuItemName, MenuItemType _menuItemType, DataSourceName _dataSourceName, Map _indexFieldValuesMap, DataAreaId _dataAreaId = curExt())
        System.Uri host                     = SessionContext::Get_Current().Get_RequestUrl();
        UrlHelper.UrlGenerator generator    = new UrlHelper.UrlGenerator();
        generator.MenuItemName              = _menuItemName;
        generator.MenuItemType              = _menuItemType;
        generator.HostUrl                   = host.GetLeftPart(System.UriPartial::Path);
        generator.Company                   = _dataAreaId;
        generator.EncryptRequestQuery       = true;

        if (_dataSourceName && _indexFieldValuesMap)
            MapEnumerator mapEnumerator = _indexFieldValuesMap.getEnumerator();

            var requestQueryParameterCollection = generator.RequestQueryParameterCollection;

            while (mapEnumerator.moveNext())
                requestQueryParameterCollection.UpdateOrAddEntry(_dataSourceName, mapEnumerator.currentKey(), mapEnumerator.currentValue());

        return generator.GenerateFullUrl().AbsoluteUri;

    public static str generateRecordUrlFromDataSource(FormDataSource _formDataSource)
        FormRun         formRun         = _formDataSource.formRun();
        str             menuItemName    = formRun.args().menuItemName();
        MenuItemType    menuItemType    = formRun.args().menuItemType();
        DataSourceName  dataSourceName  =;

        TableId   tableId   = _formDataSource.table();
        DictTable dictTable = new DictTable(tableId);
        DictIndex dictIndex = new DictIndex(tableId, dictTable.primaryIndex());

        int     fieldCount          = dictIndex.numberOfFields();
        Map     indexFieldValuesMap = new Map(Types::String, Types::String);
        Common  record              = _formDataSource.cursor();
        FieldId primaryKeyFieldId;

        for (int fieldIndex = 1; fieldIndex <= fieldCount; fieldIndex++)
            primaryKeyFieldId = dictIndex.field(fieldIndex);

            indexFieldValuesMap.insert(fieldId2Name(tableId, primaryKeyFieldId), any2Str(record.(primaryKeyFieldId)));

        return URLUtility::generateRecordUrl(menuItemName, menuItemType, dataSourceName, indexFieldValuesMap, record.DataAreaId);

To test this class, you can use simple job:

class RunnableClass1
    public static void main(Args _args)
        Map indexFieldValuesMap = new Map(Types::String, Types::String);
        indexFieldValuesMap.insert(fieldStr(VendTable, AccountNum), '1002');

        Box::info(URLUtility::generateRecordUrl(menuItemDisplayStr(VendTable), MenuItemType::Display, identifierStr(VendTable), indexFieldValuesMap, 'usmf'));

It’s an initial version, so feel free to post comments in case of any bugs!

AX 7. Trick to override method in derived class without overlaying.


The VendDocumentLineType class has several subclasses that handle specific types of vendor document lines. We want to change a behaviour for invoice lines and implement new logic for Purchase Unit field defaulting.

Solution using overlaying:

Overlay VendDocumentLineType_Invoice class and override determineDefaultPurchUnit method.

Solution without overlaying:


1) Create class derived from VendDocumentLineType_Invoice:

/// <summary>
/// The <c>VendDocumentLineType_Invoice_Sample</c> class is used for validation and applying default values to invoice lines.
/// </summary>
class VendDocumentLineType_Sample extends VendDocumentLineType_Invoice

2) Override determineDefaultPurchUnit method:

protected PurchUnit determineDefaultPurchUnit()
    // implement new behaviour
    return VendParameters::find().DefaultPurchUnit_Sample;

3) Create post event handler for VendDocumentLineType constructor to replace VendDocumentLineType_Invoice with new implementation:

/// <summary>
/// Handles events raised by <c>VendDocumentLineType</c> class.
/// </summary>

public static class VendDocumentLineTypeEventHandler_Sample
    /// <summary>
    /// Post event handler for VendDocumentLineType.createFromTable(...)
    /// </summary>
    /// <param name="_args"></param>
    [PostHandlerFor(classStr(VendDocumentLineType), staticMethodStr(VendDocumentLineType, createFromTable))]
    public static void VendDocumentLineType_Post_createFromTable(XppPrePostArgs _args)
        // get params from args
        VendDocumentLineMap vendDocumentLineMap = _args.getArg(identifierStr(_vendDocumentLineMap));
        PurchLine           purchLine           = _args.getArg(identifierStr(_purchLine));
        PurchParmUpdate     purchParmUpdate     = _args.getArg(identifierStr(_purchParmUpdate));

        // construct and init new object
        VendDocumentLineType strategy = VendDocumentLineType_Invoice_Sample::createFromTable(vendDocumentLineMap, purchLine, purchParmUpdate);

        // replace return value only if new strategy is created; otherwise use standard
        if (strategy)
           // replace return value with new implementation


4) Implement methods that will instantiate and initialize VendDocumentLineType_Invoice_Sample class:

/// <summary>
/// Constructs a new instance of a <c>VendDocumentLineType_Invoice_Sample</c> class derivative.
/// </summary>
/// <param name="_vendDocumentLineMap">
/// A <c>VendDocumentLineMap</c> record.
/// </param>
/// <param name="_purchLine">
/// A <c>PurchLine</c> table record that is used when you apply the default values; optional.
/// </param>
/// <param name="_purchParmUpdate">
/// A <c>PurchParmUpdate</c> table record that is used when you apply the default values; optional.
/// </param>
/// <returns>
/// A <c>VendDocumentLineType_Invoice_Sample</c> class.
/// </returns>
public static VendDocumentLineType_Invoice_Sample createFromTable(VendDocumentLineMap _vendDocumentLineMap, PurchLine _purchLine = null, PurchParmUpdate _purchParmUpdate = null)
    VendDocumentLineType_Invoice_Sample strategy;

    // VendDocumentLineType_Invoice is created only for this 2 types
    if (_vendDocumentLineMap.Ordering == DocumentStatus::Invoice ||
        _vendDocumentLineMap.Ordering == DocumentStatus::ApproveJournal)
        strategy = new VendDocumentLineType_Invoice_Sample();
        strategy.init(_vendDocumentLineMap, _purchLine, _purchParmUpdate);

    return strategy;

public void init(VendDocumentLineMap _vendDocumentLineMap, PurchLine _purchLine, PurchParmUpdate _purchParmUpdate)
    // code copied from VendDocumentLineType::createFromTable to init new instance
    this.physicalStrategy(VendDocumentLineTypePhysical::createFromTable(this, _vendDocumentLineMap));

You can apply this approach to any class hierarchy in AX that has construct method (like SalesLineType, PurchLineType, etc.) when you need to override methods in derived class to implement new behaviour.

AX 2012. High “System Id” Number Sequence consumption or how incorrect setup can affect AX.

Recently we did a performance analysis for one of our customers using DynamicsPerf utility. Analyzing number sequence consumption, to determine preallocation quantities, we noticed high system id usage in one of the companies (50 000 per day). We went through a list of possible entities that could consume this number sequence and realized that only sales order invoices are posted on a daily basis. However, the quantity of invoices is less than ten per day and could not be a case.

After additional investigation, we found a sales order invoice posting batch job scheduled with 1-hour recurrence.

This batch job had a late selection parameter checked and quite strange selection criteria:


Bingo! As you can see here AX should process all orders irrespectively of status. In this company we had 10000 of posted sales orders, because of incorrect setup batch job iterates through each order and creates one batch task per 5 orders (depending on setup in accounts receivable parameters, that is 5 by default). Each batch task allocates new parmId from system id number sequence. So, 10000 orders will create 2000 tasks every hour that leads to 48000 tasks per day!

This is a good example how incorrect setup may add additional overhead to overall system performance creating thousands of batch tasks and doing thousands DB calls. And the fix is pretty simple – exclude invoiced orders from selection criteria.


AX 7. Using Timer to perform a periodic task.

In previous versions of AX, we could use setTimeOut() method to execute a form method periodically (actually this method belongs to Object, so any class derived from Object could benefit from it). However, this method is deprecated in AX 7 and new setTimeoutEx() is introduced to replace it.

You may find setTimeOut() in source code, but it does nothing, and probably should cause BP error or warning.

I created a sample form to demonstrate how it works. It has only one Time control that is updated each 500 milliseconds. This form is quite similar to AX 2012 tutorial_Timer but it does not exist in AX 7 anymore.


public class TestTimer extends FormRun
    void run()
        element.setTimeoutEx(formMethodStr(TestTimer, updateTime), conNull(), 500);

    public void updateTime(AsyncTaskResult _result)
        if (!element.closed()) //otherwise will be executed even after form close.
            element.setTimeoutEx(formMethodStr(TestTimer, updateTime), conNull(), 500);

When you call setTimeoutEx() method AX adds TimerControl control to a form and executes task asynchronously.

Please note two important things here:

  1. Method called by setTimeoutEx() should accept AsyncTaskResult as a parameter or run time exception will be thrown.
  2. updateTime() method checks if a form is not closed, otherwise AX will execute this code even after the form is closed.