[D365] Fin Ops Dev tools Add ins – location of VS extension folder


When creating Visual studio Tools addins for D365 Fin ops you would need to copy it into the extension folder. This folder is most probably different on every machine.

To get the folder Open Regedit and navigate to: HKCU:\SOFTWARE\Microsoft\VisualStudio\14.0\ExtensionManager\EnabledExtensions

This stores the folder under the property DynamicsRainer*

Store your addin over here.

I have a powershell script that copies all the dll’s into that folder

Advertisements

D365 FO EDMX / OData trimmer – Make your metadata smaller


Creating a .Net application with the OData metadata from your D365 application can generate a large file

A typical metadata file (edmx) retrieved from https://<d365url>/data/$metadata will return a file of almost 17MB. Turn that into a c# file using the T4 templates and the C# source file is almost 64MB.

After spending some time trying to reduce the EDMX file to only the entities I require (and a lot of nagging by my friend) I created a way to remove the excess entities from here. Result is a smaller EDMX file, and a more lightweight .Net application for you.

Reference this Trimmed EDMX file in your T4 template

The project and instructions can be found at my Github project EDMXTrimmer: https://github.com/shashisadasivan/EDMXTrimmer

 

[D365] Operations Financial Reporter – Cannot open


Financial reporter “Yet another post if this doesnt open up”
If you get a message from the click once installer saying : “Your security settings do not allow this application to be run on your computer”

Start with running this from IE. Chrome gives some other issues
1. Add the website to your trusted sites (via internet options > Security > trusted websites > Sites and add it over there)
2. Edit the registry editor (Start > regedit OR Start + r > regedit.exe)
3. Navigate to \HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT.NETFramework\Security\TrustManager\PromptingLevel
4. Change the value for TrustedSites from Disabled to Enabled
(if this is your own system and you work on multi[ple environments, then probably change the value of Internet as well from Disabled to Enabled)
5. Save regedit and restart IE.

Note: Edge / IE12 has better chances of running the clickonce than chrome

Source: https://msdn.microsoft.com/en-us/library/ee308453.aspx

[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 dirPartyLocation.party == 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;
    ttsBegin;
    select firstonly forupdate crosscompany seq
        where seq.tabId == 123456; // use the table id here or tablenum()
    seq.skipTTSCheck(true);
    seq.skipDatabaseLog(true);
    seq.selectForUpdate(true);
    seq.nextVal = 5637123456 + 1; // enter the last recId for the table
    seq.update();
    ttsCommit;
}

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

    systemSequence.suspendRecIds(tableId);
    systemSequence.suspendTransIds(tableId);
    systemSequence.flushValues(tableId);
    systemSequence.removeRecIdSuspension(tableId);
    systemSequence.removeTransIdSuspension(tableId);
}

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

LicGenerator_V1.1
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&amp;amp;amp;amp;lt; 10; i++)
{
  // variable i can only be referenced here
}
// Try to retrieve i here and you will receive a compile error

The var keyword: ⭐

&amp;amp;amp;lt;/pre&amp;amp;amp;gt;
&amp;amp;amp;lt;pre&amp;amp;amp;gt;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()
    {
        myInt++;
        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: ⭐

try
{

}
catch(Exception::Error)
{
    // you can add multiple catch types as normal
}
finally
{
   // 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)
      DECLARE @ELEMENT_HANDLE int
      -- 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
      DECLARE @ELEMENT_HANDLE int
      -- 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:

select
S.AOTNAME, L.Text as AOTNAME, LD.Text as DESCRIPTION
from SECURITYROLE S
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
on LD.Module = SUBSTRING(S.DESCRIPTION, 2, 3)
and LD.LabelId = SUBSTRING(S.DESCRIPTION, 5, 10)
and LD.Language = 'en_us'

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