All posts by Shashi Sadasivan

[AX2012] Get Email for Customer / Vendor (with specific roles)

To get an email address for a Customer or vendor, you can use the following statement

static void DEL_SS_emailStmtjob(Args _args)
CustTable cust; //Replace with vendTable for Vendors
DirPartyLocation dirPartyLocation;
LogisticsElectronicAddress elecAddress;
LogisticsElectronicAddressRole elecAddressRole;
LogisticsLocationRole locRole;
select firstOnly cust
where cust.AccountNum == '‪‪‪Cust-001';
while select DirPartyLocation
where == cust.Party
while select elecAddress
where elecAddress.Location == dirPartyLocation.Location
&& elecAddress.Type == LogisticsElectronicAddressMethodType::Email
while select elecAddressRole
where elecAddressRole.ElectronicAddress == elecAddress.RecId
join locRole
where locRole.RecId == elecAddressRole.LocationRole
info(strFmt("%1 - %2", elecAddress.Locator, locRole.Name));

SystemSequences Update

If you ever get a error where the RecId being inserted into the table already exists then the SystemSequences table is to be blamed for this

In some of my previous blog posts, i update the system sequence table’s NextVal record, which determines the RecId to be given. However, this is tricky.

  1. Update in SQL
    • Stop the AOS
    • Update the NextVal recod in the SystemSequences Table
    • Start the AOS
  2. Write a job
static void SetNextRecId(Args _args)
    SystemSequences seq;
    select firstonly forupdate crosscompany seq
        where seq.tabId == 123456; // use the table id here or tablenum()
    seq.nextVal = 5637123456 + 1; // enter the last recId for the table

You may need to run this job and restart the AX client only if the just the above doesn’t work

static void sab_recIdSequenceFix(Args _args)
    SystemSequence systemSequence = new systemSequence();
    Tableid tableId = 123456; // use the table id or tablenum() here


AX 2012 License generator UI

So i had created a UI for the license generator.

To create a license in 2012 this was the command:

axutil genlicense /file:c:\licenses\MyLicense.txt /certificatepath:c:\certificates\MyCertificate.pfx /licenseCode:MyModule /customer:MyCustomer /serialnumber:1231232123 /password:MySecretPassword123 /expirationdate:12/30/2018 /usercount:5

However, having the password and certificate path information available wasnt exactly a good idea at that time (and probably is still relevant. Why would you want to distribute those two together no?)

So came the the License generator, with the password hard coded and everything else seemingly invisible to the person punching the licenses without having someone with knowledge of the axutil and more importantly

What is this black box ? and why cant I just click somewhere

Moving forward a few years later and opening up the code out, (yes the password has been taken out of the code). Its now stored as well, although not very secure, but enough to keep prying eyes out for a bit. (this means rooms for improvement)

The code is published at GitHub under AxLicenseGenerator (See the readme in that link)

Or you can Download Ax License Generator 1.1

There is lots more information there, or message me if you need more information.

Happy Licensing

License Generator v 1.1

X++ and Ax7 Whats Changed

A lot of changes have taken place in the X++ language and to align it close to the C# model which is where we want to be. There is no longer any p-code compilation, everything is run from the CIL.

This my compilation of what has changed and some examples for them as well.

Editing Experience: Visual studio interface only. Finally a more structured way of viewing code.

Declare anywhere

for(int i = 0; i< 10; i++)
  // variable i can only be referenced here
// Try to retrieve i here and you will receive a compile error

The var keyword: ⭐

<pre>var counter = 0;
var custTable = salesTable.custTable();

Static constructor and fields: Static member variables Static constructors: TypeNew

class AClass
    static string staticString;
    static void TypeNew()
        AClass::staticString = "Static World!";

Macros: As i suspected, try avoiding these guys. Will be deprecated in later releases (my view) const (constant) and readonly:

class Square
    public const int Sides = 4; // this value cannot be changed
    readonly real _length = 10; // this is a default

    void new(real length)
        this._length = length; // readonly can be changed in constructors only
Field Access: :star:
Field access was only protected in Ax (no questions asked, and nothing else you can do about it)
Now: if you havent specified the access they are still protected, but you can specify public, or private
class MyVariables
    int myInt;
    protected int myInt2;
    public string Name;
    private real _qty;
class MyVariables2 : MyVariables
    void WhatCanIDo()
        myInt2 = myInt + 10;
        Name = "My New World!";
        _qty = 100; // Compilation error

class AccessVariables
    void WhatCanIDo()
        var myVariables = new MyVariables();
        myVariables.myInt = 10; // Error
        myVariables.myInt2 = 100; // Error
        myVariables.Name = "Hello";
        myVariables._qty = 100; // Error

Access variables by reference
Lets the above example

class MyVariables
    int myInt;
    public string Name;

    public void AccessVarisables()
        this.myInt = 5;// This references to itself,and works with intellisense 🙂
        this.Name = "Hello World!";
class Access
    public AccessVariables()
        var myVars = new MyVariables();
        myVars.Name = "Hello New world!"; // Name is accessed via the class object,no more Parm methods required;

try / catch / Finally: ⭐


    // you can add multiple catch types as normal
   // this code always gets called. So you can do things like close file handles

using statement: ⭐
Using is here, this is very neat. It destroys the object and the object only exists within the context of the using. Object in the using should implement IDisposable, so all / most of the Streams are an example or webRequests.

using(var sw = new System.IO.StreamWriter("file.txt"))
     sw.writeLine("Hello World!");

Implement .Net Classes:
Classes in X++ can implement the C# classes 🙂 , so your X++ class can implement IDisposable

Extension Methods:

There are very similar to C# extensions, except the convention is to end the class with _Extension and not Extension

static class HelloWorld_Extensions // Convention is to have _Extension
// This adds a method to the salesTable called SalesString returning a string
// salesTable.SalesString()
public static str SalesString(SalesTable salesTable)
return salesTable.SalesId + "-" + salesTable.CustName();

// you can add more extensions here targetting different types

Declarative Eventing (Methods and objects):

Ax 2012 gave us the ability to add events to methods. This is still the same, i.e. events still exist, exept they are called declaratively (i.e. in code and no clicks).

Although, the way to access arguments is still not to my liking and even though it seperates code out a lot, any errors are at runtime only, which can be very “hard” to manage

Casting is standard now:

// Planet class is implemented by class Earth.
var Earth = new Planet(); // this is incorrect, used to work before

[AX2012] Moving Project to a diferent Model


Moving a project to a different model causes Ax to move all the objects inside it to that model as well.

You could instead create a new project in the Model you want it and then drag all the objects into that. You will lose all the metadata of the project including timestamps.

Another way is to move just the object related to the Project to the right model.

For that use the following steps

    1. Copy the Name of the Project
    2. Find the ModelId you want to move it into. You can use Get-AxModel command for this. (this should be within the same Layer)
    3. Open SQL and find the Element handle of the Project. Use the following command to do that
      -- Find the Element Handle
      -- ElementType 37 = SharedProject
      select top 10 * from ModelElement
      where Name = 'PUT_YOUR_PROJECT_NAME_HERE'
      and ElementType = 37
    4. Once you get just one record from the above code, then run the following command to make sure that the element is the correct one (It should have the modelId which is the current model it is in)
      -- Set the Element Handle
      set @ELEMENT_Handle = (select ElementHandle from ModelElement where name = 'PUT_YOUR_PROJECT_NAME_HERE')
      select @ELEMENT_HANDLE
      -- Find the element data and check the Model id
      select * from ModelElementData where ElementHandle = @ELEMENT_HANDLE
    5. If you get just one record then we should be good to move the element to the right model
      -- Set the Element Handle
      set @ELEMENT_Handle = (select ElementHandle from ModelElement where name = 'PUT_YOUR_PROJECT_NAME_HERE')
      select @ELEMENT_HANDLE
      -- Find the element data and check the Model id
      select * from ModelElementData where ElementHandle = @ELEMENT_HANDLE
      -- Move the Model
      -- Change 20 to the ModelId you want the element to be in
      Update ModelElementData set ModelId = 20 where ElementHandle = @ELEMENT_HANDLE


[AX2012] Fetching a Label value from SQL

Coming from my previous blog post Get list of security roles with name , I extracted the label via SQL. I had to break up the Label ID into the Module and then the number.
This allowed me to then query the table ModelElementLabel to get the label value

Following code can get the Label via SQL
NOTE: you need to query the Model database for this

DECLARE @LabelStr as nvarchar(50)
Set @LabelStr = N'@SYS12345'

select * from ModelElementLabel L
    where L.Module = SUBSTRING(@LabelStr, 2, 3)
          and L.LabelId = SUBSTRING(@LabelStr, 5, 7)
        --and L.Language = 'en_us' -- Uncomment this line to filter by language

This way you wont need to search a label value from within Ax

[AX2012] Get list of security roles with name

To get export the security roles along with the AOT name of the security will need you to either write something in X++ or go into SQL

I went the SQL route and this is my code:

Left outer join ModelElementLabel L
on L.Module = SUBSTRING(S.Name, 2, 3)
and L.LabelId = SUBSTRING(S.Name, 5, 7)
and L.Language = 'en_us'
Left outer join ModelElementLabel LD
and LD.Language = 'en_us'

This will give a list of the AOT names and the Label name and Description

Power BI for Desktop & Ax 2012

Power BI for desktop is a very powerful tool and can be used to mash up data from different sources. It is definitely one up to the Excel plugins and so far seems to be more lightweight and processes things faster
I will go through my findings with PowerBI and Ax to come up with some examples

Install PowerBI: Download PowerBI for Desktop and there its a usual click  install process. There are other versions available for every other device, which i will look at later. There is a detailed article on installing powerBI at c-sharpcorner.

Open up the the application and we shall create a report that looks like this

powerBI_AxSales_OverviewAX Sales over time / Region

So once we open up PowerBI, select “Get Data” and select SQL server database

Select the data source

Enter the Database Server, if possible put the database name as well. Ax Databases being so large seems to slow down power BI.

Select the database server and database
Select the database server and database

We will now select the Tables/Views from the list. You can use the search bar and select the checkbox. Select the following Tables/Views:

  • CustInvoiceJour
  • CustGroup
  • LogisticsPostalAddressView

After selecting the checkboxes, click the “EDIT” button in the pop up window. This will load the data preview and open the “Query Editor”

Edit Table Columns

Edit the following tables to trim the data being retrieved:

  1. LogisticsPostalAddressView
    1. Select the columns: City,CountryReqionID,RecId. Then right click and select “Remove other columns”
  2. CustGroup
    1. Select the columns: CustGroup,Name,DataAreaId. Then right click and select “Remove other columns”
    2. Add a computed field for identification. Click “Add Column” from the ribbon and then “Add custom column”. Set the new column name to CustGroupId and the formula as =[CUSTGROUP]&[DATAAREAID]
      Create CustGroupId field for identification
      Create CustGroupId field for identification

      Click Ok and this field will be added

  3. CustInvoiceJour
    1. Select fields: Custgroup, OrderAccount, InvoiceAccount, InvoiceDate, InvoiceAmountMST, SalesBalanceMST, SumTaxMST, InvoicePostalAddress, CreatedBy, DataAreaId, Partition, RecId. Right click and click “Remove Other columns”
    2. Create a new custom column “CustGroupId” similar to the one done for CustGroup Table
    3. Create a new custom column “InvoiceYear”. the

Once the above is done, Select “Close and load” button, located in the “home” ribbon bar


So now that the tables have been defined, we need to set the relationships between them. Relationships in Power Bi, just like powerview in Excel, can only be linked via one field. Hence, why we added a computed column in the tables CustGroup and CustInvoiceJour

Click on “Manage relationships”

Delete any relationships in there (Currently there is one based off the custgroupId, but we shall delete it to make sure we add it all correctly)

  1. Relate CustInvoiceJour > CustGroup
    1. Add a new relation linking CustInvoiceJour to CustGroup using the CustGroupId on both Tables.

      Relation between CustInvoiceJour and Custgroup
      Relation between CustInvoiceJour and Custgroup
  2. Relate CustInvoiceJour > LogisticsPostalAddressView
    1. Add a new Relation and related CustInvoiceJour to LogisticsPostalAddressView using the InvoicepostalAddress to the RecId. See image for details

      Relation between CustInvoiceJour and LogisticsPostalAddressView
      Relation between CustInvoiceJour and LogisticsPostalAddressView

You should now have 2 relations setup. Close the Manage relations window and we will create our report

Creating the report

Bar Chart

in the field list at the right side of the screen, select the fields Name from the Custgroup table and then the InvoiceAmountMST field from the CustInvoiceJour Table. This will create a Table. Select the Bar chart option on the right side and it will change the view


Click on a blank area in the report. From the field list select InvoiceAmountMST from the CustInvoiceJour Table and then CountryRegionId from the LogisticsPostalAddress. Then select the Map view

Sales by region over time

This will show how sales figures have been flowing by the countries
Click on a blank area in the report canvas. From the field list select InvoiceAmountMST and InvoiceYear from the CustInvoiceJournal. Make sure the InvoiceYear is on the X-Axis. Then select CountryRegionId from LogisticsPostalAddressView. Change the graph type to Line

Hoping to create more reports and make them available

AX 2012 Install Model – A Checklist

This is a checklist I follow to install Models Provided for a installed site

  1. Stop the AOS
  2. Backup the Database
  3. Install models (follow guidelines, you may be told to use the overwrite option)
  4. Merge Code (optional)
    1. Start AOS
    2. Merge Code
    3. Stop AOS
  5. Compile X++
    1. Option 1 – AxBuild.exe (RU7 and Onwards)
      1. Make sure AOS is turned off
    2. Option 2
      1. Start AOS
      2. Open Ax Client
      3. Compile
      4. Stop AOS
  6. Compile CIL
    1. Start AOS
    2. Open Ax Client
    3. Run Full CIL Compile
  7. Restart AOS
  8. Synchronize database
  9. Restart AOS

If you do restore a Live database over the TEST database, make sure to transfer the parameters as well as reset the SYSSQMSettings.GlobalGUID

AX2012 – Compare model. When you put in hotfixes, updates and want to do less – Part 1

via Tamás Mészáros

So you got updated Model(s) or hotfix(es) and need to make sure your ISV or VAR or CUS / USR layer code has been merged.

Lets see what your options are:

  1. Create a project for each model and then compare then to the upper layers for changes.
    — You spend a lot of time giving yourself carpal syndrome
  2. Auto code upgrade
    — It only manages hotfixes. The code that needs to be manually reviewed can be overwhelming? (someone please correct me on that)
  3. You write some script to find out how to do this
  4. Magic beans
    — You don’t get them

Ok, so I wrote some SQL scripts to do this, however it was getting too complex for me and I knew I could do it faster with in memory processing with C#. Although I can argue with myself that SQL can do a better job with this. However, I went to the path of C#

Tables used:

  1. SysElementType – This stores what a node in the AOT is classified as. E.g. TableInstance, ClassInstanceMethod, BaseEnum. This is purely to translate an integer to a human readable value.
  2. SysModelElement – This is where the definition of each object is. So every node in the AOT is one record here. This record also links it to the Parent Element. So a record which is the table field, will have its parent element to the Table itself. A parent element can have another parent element too, e.g. a form element inside a group
  3. SysModelElementData – This is where the code for the SysModelElement is stored. And it it stored for each Model (not layer). Moment I override the code in the USR layer for the SalesFormLetter classdeclaration in Ax, it will create a record in this table with the new code. So use it as a caution, if you ever query this table, be careful because you could be transferring a “lot” of data across the wire

Combining SysModelElementData, i can find what all other models use the same element. Then link it up to sysModelElement and find more information about the AOT object that has changed.

Armed with this knowledge I went and created  C# console application

The project currently compares models. So if you want to know what has affected the VAR and USR layer models with the addition of a particular SYP layer model, then you send the list of SYP model id’s and then the list of VAR and USR layer models as a csv string. This does a fast compare and outputs what objects you need to look at.

The shortcomings / bugs of this so far:

  1. Security, menu items dont get compared because of the way this is stored
  2. Currently you need to compare models. I would like to say i need to know what objects are affected by these SYP models and then get a list of objects on all the higher layers
  3. Need to create a library instead of a console project

This project is posted on GitHub: