Integrate SharePoint Online with Dynamics AX Legacy Application

SharePoint Online is a wide spread solution for collaboration. Actual versions of Dynamics AX / 365 provide great integration capabilities and for all other purpose there exists the SharePoint Online Client API. However, legacy applications may not support the required libraries or .NET framework versions to use the SharePoint Online Client API. One way to overcome this issue is to utilize the REST API and communicate via HTTP. Here you can find a step by step guide how to provide an external application access to SharePoint Online via an App:

Register an App in SharePoint Online

The first thing you need to do is register an App in SharePoint Online. Open the registration site in a browser: . In my case I’m using a sub site, therefore my URL would look like this: .Use the form to generate a client ID and a secret code. Provide any title you like. Set the domain to localhost and redirection URL to https://localhost


Set Permissions for the App

Open the following URL in a Browser: . Use the Client ID to lookup the created App. In the free text form add the required permissions e.g. access a list. You can find the syntax here: If you need access to more than one list, you have to repeat this step. In my case I only need access to one list. The XML Permission Code looks like this:

<AppPermissionRequests AllowAppOnlyPolicy="true">
Right="Read" />

Click the Create button, this will forward you to a form where you can choose which list can be accessed by the app.


SharePoint Online Tenant ID

To find the Tenant ID open the following URL in your Browser  Since I’m using a sub site my URL looks like this . There you can find the Tenant ID


Create a Token

To interact with SharePoint Online you need to generate a token.This is done by sending an HTTP POST requiest with client ID, secret, and desired resource to SharePoint Online. Here is the Code in X++ written in Ax 2009.

str clientID = „<YOUR_CLIENT_APP_ID>“;

str secret = „<YOUR_SECRET>“;

str tenant = „<YOUR_TENANT>“;

str ctx = „00000003-0000-0ff1-ce00-000000000000“; //i.e. SharePoint

str sp = „<YOUR_SITE>“;

str listName = „<NAME_OF_YOUR_LIST>“; // e.g. Customers

str listUrl = “‚“+listName+“‚)/items; “

str url = „“


System.Net.WebClient cl = new System.Net.WebClient();

System.Net.WebHeaderCollection headers = new System.Net.WebHeaderCollection();

System.Collections.Specialized.NameValueCollection body = new System.Collections.Specialized.NameValueCollection();

System.Byte[] response;

System.Text.Encoding enc = System.Text.Encoding::get_UTF8();

System.String responseText;

str xppResponse;

int indexOf;

int start;

int totalLength;

int length;

str token;








response = cl.UploadValues(url,“POST“,body);

responseText = enc.GetString(response);

// cut token out of response text

indexOf = responseText.IndexOf(„access_token“);

start = indexOf + 15;

totalLength = responseText.get_Length();

length = totalLength – start – 2;

xppResponse = responseText;

token = responseText.Substring(start,length);


Query List Elements

The token has a valid timestamp measured in Unix Ticks. So it might be good idea to store the token in a parameter table and only request a new one if it is or will expire soon. However, in this example I go on and query the Customer List.

// .. Code from above

cl = new System.Net.WebClient();

headers = new System.Net.WebHeaderCollection();


headers.Add(„Authorization“,“Bearer „+token);



url = listUrl;

responseText = cl.DownloadString(listUrl);

xppResponse = responseText;



In this case the response type is an XML. You may parse the XML and only view e.g. the Title Tag or any other fields your are interested in. Here is the Infolog of the complete XML response from the SharePoint Online List.



Walkthrough: Configure XML/File Data Export via AIF in AX 2012

This is an example how to export the LedgerJournalTable + LedgerJournalTrans records as XML via AIF

Create a Query

  1. Create a new query and name it AxdLedgerJournal.
  2. Place the LedgerJournalTable as main Datasource
  3. Set the Fields Property Dynamic to Yes
  4. Add the LedgerJournalTrans as child data source
  5. Set the Fields property Dynamic of the LedgerJournalTrans data source to Yes
  6. Set the Relation property of the LedgerJournalTrans data source to Yes
  7. Set the Fetch Mode property of the LedgerJournalTrans data source to 1:n
  8. Set the Join Mode property of the LedgerJournalTrans data source to Outer Join

Document Query


Use the AIF Document Wizard to generate the service classes

  1. In the menu bar go to Tools > Application Integration Framework > Create document service
  2. Provide the AxdLedgerJournal query as source query for the document service
  3. Select  the options to generate the Read and Find methods
  4. Select the option to generate the AxBC classes
  5. Let the wizard generate the software artifacts for you

AIF Wizard

Fine tune the generated code

  1. Go to the private projects and open the newly generated project
  2. Compile the project
  3. If the cache() methods don’t compile delete them
  4. Review the open tasks and provide meaningful labels
    Fine tune AIF Code

Configure the Batch Jobs

  1. In AX go to System Administration > Inquires > Batch Jobs > Batch Jobs
  2. Create a new Batch Job and go to “View Task”
  3. Add four task and choose the following classes
  4. AifGatewayReceiveService
  5. AifGatewaySendService
  6. AifInboundProcessingService
  7. Close the Task window and set the Recurrence of the Batch Job to run e.g. all 5 minutes

AIF Batch Jobs


Configure the File System Adapter

  1. In AX go to System Administration > Setup > Services and Application Integration Famework > Outbound Ports
  2. Create a new Outbound Port
  3. Select the FileSystemAdapter as Adapter
  4. Provide a target location where to place the files e.g. a share with write permissions for the AOS account
  5. Open the Service Operation Dialog and add the method
  6. Activate the Port

AIF Outbound Port with File System Adapter

Add a send method

On the LedgerJournalTable add the following method

server void sendElectronically(XMLDocPurpose _xmlDocPurpose,
                               AifSendMode _aifSendMode = AifSendMode::Async)
    AxdSendContext      axdSendContext = AxdSendContext::construct();
    AifEntityKey        aifEntityKey   = AifEntityKey::construct();
    Map                 keyData;
    AifConstraint       aifConstraint = new AifConstraint() ;
    AifConstraintList   aifConstraintList = new AifConstraintList();
    keyData = SysDictTable::getKeyData(this);





Test the export

Create a job and use the following code to test the export

static void JobTestAifExport(Args _args)
    LedgerJournalTable journal;
    journal = ledgerJournalTable::find("00001");

In AX got to System Administration > Periodic > Services and Application Integration > Queue Manager. You will find a record for the LedgerJournal export. The Batch Job will first create an XML Document and in a second step perform the export to the file system.

AIF Queue Manager

Last Day Of Month and Year as View Computed Column in AX 2012

View Computed Columns in Dynamics Ax 2012 support the use of TSQL functions to calculate additional view columns. Here is  an example how to calculate the Last Day of Month and Last Day of Year based on the TransDate at the CustTransView. First, Extend the SysComputedColumnDatePart  Base Enum and add two additional values Month and Year. Extend the getDateDiffFormatSelector() method at the SysComputedColumn class.

private static server str
getDateDiffFormatSelector(SysComputedColumnDatePart _dateDiffFormat)
        case SysComputedColumnDatePart::Day :
            return ‚d‘;
        case SysComputedColumnDatePart::Second :
            return ’s‘;
        case SysComputedColumnDatePart::Month:
            return ‚m‘;
        case SysComputedColumnDatePart::Year :
            return ‚yy‘;

    return ‚d‘;


Last Day Of Month

Add a new static method to the view and call it lastDayOfMonth.

private static server str lastDayOfMonth()
    DictView    ctView = new DictView(tableNum(‚CustTransView‘));
    str sTransDate = ctView.computedColumnString(‚CustTrans‘,’TransDate‘);
    str sLastDay = "";

    sLastDay = SysComputedColumn::cast(
    ‚0‘,sTransDate,SysComputedColumnDatePart::Month)+ ‚+1′,’0‘,

    return sLastDay;

Add a new DateTime column to the view and set the lastDayofMonth() method as ViewMethod

Last Day of Year

Add a new static method to the view and call it lastDayOfYear

private static server str lastDayOfYear()
    DictView    ctView = new DictView(tableNum(‚CustTransView‘));
    str sTransDate = ctView.computedColumnString(‚CustTrans‘,’TransDate‘);
    str sLastDay = "";

    sLastDay = SysComputedColumn::cast(
    (‚0‘,sTransDate,SysComputedColumnDatePart::Year) + ‚+1‘,

    return sLastDay;

Add a new DateTime column to the view and set the lastDayOfYear() method as ViewMethod

The View

Dynamics AX 2012 translates both fields in TSQL Code. The view looks like this



The result in Dynamics AX looks like this

CustTransView with Computed Column

Visualize the firms contribution to total revenue in Power BI

We recently discussed at work how to display the contribution of multiple firms to the total revenue in Power BI, in a way that the firms can be filtered but the total revenue stays constant. We came up with a very simple no-code solution.

Data from Dynamics AX 2012 R3

All the required data can be fetched from the CustInvoiceJour table in Dynamics AX. There are three datasets in the Power BI data model. First, the invoices including the InvoiceAmountMST, InvoiceYear and DataAreaId. Second the sum of InvoiceAmountMST grouped by Year and DataAreaId. Finally, a table that contains the years. Here are the SQL statements to load the data.

InvoiceId, InvoiceAmountMST, InvoiceDate, DataAreaId,
year(InvoiceDate) as InvoiceYear
from CustInvoiceJour

Total Revenue:

sum(InvoiceAmountMST) as InvoiceAmountTotal,
year(InvoiceDate) as InvoiceYear, DATAAREAID
from CustInvoiceJour
group by year(InvoiceDate),DATAAREAID

In Power BI create a new dataset and enter the years manually, like 2010, 2011, 2012, etc.

Power BI Years Table

In Power BI go to the data model an connect the Invoices and Total Revenue with the dataset that contains the years


Line and stacked Column

Add a new Line and stacked Column chart to the Power BI Report. Set the field Year from the Year table as shared axis. From the CustInvoiceJour set the DataAreaId as Colum Series and the InvoiceAmountMST as Column  values. Set the InvoiceAmountTotal from the Totals table as line values. In the format tab disable the Y-Axis property Show Secondary.

Diagram settings 

Your diagram should look like this. Filtering the companies does not effect the total revenue line.


Integrate an USB Scale with Dynamics AX

I was recently playing with an Dymo USB scale and how to connect it to a Dynamics AX 2012 instance on a virtualized HyperV installation. It turned out that connecting these two is not so hard at all. The Dymo scale was immediately recognized by my PC.

Dymo scale connected to a Windows 10 PC

To access the USB scale, you need to know the Vendor ID and Product ID. This can be found in the Windows device manager. In my case the Vendor ID is 0x0922 and the Product ID is 0x8003

USB Vendor ID and Product ID in device manager

Access the USB Scale via C#

You need the Human Interface Device library, which is also available as source code. Download the Visual Studio project, open the .sln Solution from the SRC folder and build the library:

Some code is needed to access the scale. Fortunately, there is a code snipped available here: .

  • Create a Visual Studio C# DLL Project.
  • Copy the code for the USBScale class from the website.
  • Add the previous built HIDLibrary to your projects references.
  • Add two int properties to the class, for Vendor ID and Product ID.

    public class USBScale
        public int VendorId { get; set; }
        public int ProductId { get; set; }

  • Change the following line in the GetDevices() method at the USBScale class

public HidDevice[] GetDevices()
   //return HidDevices.Enumerate(0x0922, 0x8004).Cast().ToArray();
   return HidDevices.Enumerate(VendorId, ProductId).Cast<HidDevice>().ToArray();


  • For convenience, add a new method GetGramm()

    public decimal GetGramm()
        decimal? lb = -1;
        decimal? gr = -1;
        decimal? oz = -1;
        bool? stable = false;

        GetWeight(out lb, out gr, out oz, out stable);

        if (gr.HasValue)
            return gr.Value;
        return -1;


  • Build the DLL library

Forward the USB device  to HyperV

There are different ways to pass an USB device to a virtual machine. However, I was using a tool called USB Redirect which is available as trial version for testing. It has two components. The server which manages and shares the USB devices is installed on the physical machine.

USB Redirect

The client is installed on the VM and can access the USB device at the physical machine.

USB Redirect Client


Integrate with Dynamics AX

Finally, the last step to integrate with Dynamics AX is easy. Copy the HID Library DLL and the USBScale DLL to the client bin folder. Create a form with a Scale button. At the clicked() method create a new instance of the USBScale class, provide your product and vendor ID and call GetGramm().

<YOUR_NS>.UsbScale scale = new <YOUR_NS>.UsbScale();
real value;

value = scale.GetGramm();

info(strfmt(“%1 g”,value));

Dynamics AX with USB Scale

Performance Optimization by using Included Columns and Field Selects

Since version 2012, Dynamics AX supports included columns in indices although SQL Server supports it  for quite a long time. Here are some examples how and why it is good practice to use included columns in an index. I’m using Dynamics AX 2012 R3 Cu12 on Windows Server 2016 and SQL Server 2016 with Contoso Demo data for this example

Cluster Index.

The cluster index can be defined using multiple fields and is used to defined the order of records stored in the table. Even more important is the fact, that if a table has a clustered index all the data is stored in the table, i.e. the cluster index IS the table!


Take a look at the space allocated by the indices. About 219 MB are used to store actual data and 167 MB are used to store index information


The following SQL Statement reveals the size in detail

SUM(s.[used_page_count]) * 8 AS IndexSizeKB
sys.indexes ind
sys.tables t ON ind.object_id = t.object_id
sys.dm_db_partition_stats AS s ON s.[object_id] = ind.[object_id]
AND s.[index_id] = ind.[index_id]
order by IndexSizeKB desc

The table data is stored in the TransOriginIdx

name IndexSizeKB
I_177TRANSORIGINIDX    226992 ~ 221 MB
I_177ITEMIDX 24872
I_177RECID 23416
I_177DIMIDIDX 22192

Index Usage with Field Select

Here is an example of a select statement with field select on the InventTrans table

while select ItemId,DatePhysical
from inventTrans
InventTrans.ItemId == ‚0001‘ &&
inventTrans.DatePhysical >= str2Date(‚1.1.2011‘,123)

{ .. }

The trace parser reveals the actual SQL Statement sent to the database


What happens is what you would expect, SQL uses the ItemIdx for this query


Only 5 logical reads where necessary



Select Non-Index fields

When the query selects fields which are not part of the index, SQL server has to perform a lookup in the Cluster Index for each record identified by the ItemIdx to get all the other fields. For example the Voucher and Qty are not part of the ItemIdx.


213 logical reads were necessary to fetch the data


This can get even worse, when performing the lookup becomes to expensive. This can happen when the query returns a larger number of records. For example, when querying for another ItemId. In this example SQL server does not use the ItemIdx anymore, but performs a search in the clustered index instead. The ItemIdx became completely useless for this query.


SQL server required 1345 logical reads to fetch the data!



Included Columns

Since version 2012 Dynamics AX supports the definition of Included Columns for indices. These columns are not used to sort the index. These are just fields which are stored within the index to avoid costly lookups in the clustered index. In Dynamics AX you just add columns to the index and set the property IncludedColumn to Yes.


You can find the included columns in SQL server when viewing the properties of the index


When the statement from above is executed again, SQL server can use the included columns from the index and does not perform costly lookups in the clustered index.


Only 6 logical reads are required to fetch the data. This is a huge optimization compared to the 1345 reads without included columns.


Add a calculated field to an AIF Document

AIF is great for application integration and providing external applications required data. Document exports can be generated very easily by providing a query to the AIF document wizard. However, if you want to provide not only table fields but calculated values (e.g. display methods) in an AIF document some work is required. This is an example how to add the CustInvoiceJour.contributionMargin() method to the SalesSalesInvoiceService.

Parameter methods

Add the following methods to the AxCustInvoiceJour class. (You may duplicate the parm* and set* method from an existing field e.g. InvoiceAmount and change the name and parameter.)

public AmountMst parmContributionMargin(AmountMst _margin = 0)
return custInvoiceJour.contributionMargin();

protected void setContributionMargin()

Add a macro in the class declaration of the SaleSalesInvoice_CustInvoiceJour class

class SalesSalesInvoice_CustInvoiceJour extends AfStronglyTypedDataContainer
// lot more here ..



Add the following methods to the SaleSalesInvoice_CustInvoiceJour class

public boolean existsContributionMargin()
return this.exists(#ContributionMargin);

public AmountMST parmContributionMargin(AmountMST_value = 0)
return this.get_Attribute(#ContributionMargin);

Refresh the schema definitions

Open the AIFDocumentSchemaTable in the table browser and delete the record for the DocumentName SalesInvoice.

Delete the SalesInvoice record from the AIFDocumentSchemaTable

In the AOT navigate to the SalesSalesInvoiceService. From the context menu choose to register the service. This will populate the AIFDocumentSchemaTable with the new XML schema including the new field.

Don’t’ forget to activate the new field in your endpoint. Navigate to Basic > Setup > Application Integration Framework > Endpoints > Action Policies > Data Policies > Data Policies and active the new field.

Activate the new field in the AIF data policies

Now you’re ready to test your work. In this example navigate to Accounts Receivable > Inquiries > Journals > Invoice and click on send electronically. Depending on your AIF configuration, check the output. Your new field should be there. Here is the XML from AIF Queue Manager processing an AIF message to a File System Adapter:

ContributionMargin in CustInvoiceJour AIF document