Wednesday, February 22, 2017

X++ code to read CSV files in Dynamics 365 for operations


Reading data from csv files is a very common development requirement for X++ developers. In Dynamics 365 for operations there are some interesting concepts, as opposed to AX2012. The application runs on cloud and the client is in web. In such scenarios the way to read files from local system is a 2 step process.
Step 1: Upload the file from local machine to Microsoft cloud (Azure) storage space called as Azure blob.
Step 2: Read the file from Azure blob to retrieve the data.
 
Microsoft has done an excellent job in creating out of the box classes to run this logic. So we are not required to re-invent the wheel and can leverage the existing API's. The below X++ code can be used to read a CSV file:

 
 
The standard API's are used here do the required magic. I have tried to breakdown the functionality of the calls.
Step 1 is done by the below command where the file is uploaded from local machine to azure blob


 
Step 2 is done by opening the IO stream from the Blob and reading it from the IO class

 

Rest of the job is almost similar to AX2012 pattern.
 
To test the job we create a sample csv file

 
 
On running the job a file picker will be shown, where you can browse the file.



 
The progress of uploading it to cloud space is shown in the progress bar


 
 
 
 
Infolog is shown with the data on the file


 
 

Worth mentioning that there are various other IO classes, provided out of the box which have the below associations
 

 
 
The file upload classes have different strategies and the below classes are available out of the box. For more information refer to nice articles mentioned in the references:


To quickly reuse the code here it goes

class RGReadSample
{       
    ///
    /// Runs the class with the specified arguments.
    ///

    /// The specified arguments.
    public static void main(Args _args)
    {       
        AsciiStreamIo                                   file;
        Array                                           fileLines;
        FileUploadTemporaryStorageResult                fileUpload;

        fileUpload = File::GetFileFromUser() as FileUploadTemporaryStorageResult;
        file = AsciiStreamIo::constructForRead(fileUpload.openResult());
        if (file)
        {
            if (file.status())
            {
                throw error("@SYS52680");
            }

            file.inFieldDelimiter(',');
            file.inRecordDelimiter('\r\n');
        }

        container record;
        while (!file.status())
        {
            record = file.read();

            if (conLen(record))
            {
                info(strFmt("%1 - %2",conPeek(record,1),conPeek(record,2)));
            }
        }

        info("done");
    }

}

Feel free to comment on any feedback/suggestions in case I have missed any important part of this framework  but this code works nicely on reading csv files. Thanks for reading the blog and have a great day.

More references:
https://ax.help.dynamics.com/en/wiki/file-upload-control/
http://dev.goshoom.net/en/2016/03/file-upload-and-download-in-ax-7/

 

Tuesday, January 24, 2017

Extension methods in X++

In Dynamics 365 for operations (D3FO), one of the main emphasis of Microsoft is to develop extensions and avoid doing any customization to standard objects shipped by Microsoft. A new X++ feature called  Extension Methods is introduced to enable developers to create new methods for standard AX objects without customising them.

Extension methods comes from C# where they enable you to "add" methods to existing types without modifying the original type. These are a special kind of static method which are called as if they were instance methods on the extended type.  MSDN reference Extension methods in C#

In context of AX, extension methods have similar characterstics. Lets take a very common exmaple. If we have to create a new method on a  customer table which returns the customer name and telephone number as one concatened string. Normally we would create a new method on a standard CustTable object which will return the required result somehting like below.




Now there is nothing wrong in such method till AX2012 version but in the new AX (D3FO) doing this will fall under the umbrella of customising the standard AX object. This is not recommended in D3FO as it comes with lot more pains on managing such developments when installing new updates and upgrading the system.

Now in order to do this as an extension we will need to do the following:

1. Create a new static class. The class name should end with suffix _Extension
2. Creat a new static method which returns the type of data you want.
3. In this method definition the first parameter will be the CustTable object as shown below.


Now we can use this method as a table method when writing code. See below the intellisense will show the new method as a list of available method for that object.



By taking the above approach we can develop new methods and reference them via standard objects in our code without customising the objects. This is in alignment with Microsoft's recommendations for development via extensions.
 

Sunday, May 15, 2016

Using X++ Delegates in the new Dynamics AX (aka AX 7)


In this post lets walk through an example on how we can use delegates in the new AX to develop customizations.

Quick summary of delegates: X++ delegates expose the publisher - subscriber pattern where a delegate defines a clear contract in a publisher class. It is a great way of encapsulating a piece of code. Delegates were introduced in AX 2012 couple of years back. With the new AX release delegates are the recommended way for customizing standard AX classes.

Let’s jump into action and customise standard AX sales order confirmation process. To keep it simple we will the change value of a field in confirmation journal table. The idea here is how to develop delegates and call them.

Confirmation journal header data is stored in CustConfirmJour table and it is initialised in the below class method during the sales confirmation posting process. Let’s modify it using delegates.



First we create a new delegate method in this class. This serves the purpose of defining a contract between the delegate instance and the delegate handler. There is no business logic inside the delegate method. Also notice that the delegates have return type as Void. In order to access the result value we have to pass EventHandlerResult object as a parameter.
 
 

 

 
Now we modify the actual method, declare the Event handler result object and call the delegate with the parameters in the method which we need to customise . The only customization in this method is 2 lines of code as highlighted below:
 
 

 
The class structure looks as below:



 Now we create the event handler method and this is where MS has done really nice stuff in moving the AX development environment to Visual Studio. Right click on the delegate method and copy the handler method definition.




Create a new class which will be used to subscribe to the delegate. So we create a new class, let call is salesConfirmJournalExt and paste the copied clipboard text
 
 

 


The delegate handler definition is automatically added with the below information:
 
 

Now we can add our custom code in this method. I just changed the purchase order field value and added some Infolog. Note that I am actually not returning anything in this method and not using the eventHandlerResult object really.


 So we are done. The see it wokring let's build the solution and confirm a sales order.



During the process the  Infolog messages we added in the delegate handler method are shown


The confirmation journal has the custom text appended to it in the field we used in our new class method.
 
 

Microsoft strongly recommends to use Delegates for customization due to all the good reasons of having minimum code changes in standard product. So try to use delegates to have a cleaner and manageable solutions.

Feel free to share your feedback.  Below are some good online references on Delegates:
 
https://ax.help.dynamics.com/en/wiki/delegates-for-migration/

https://blogs.msdn.microsoft.com/x/2011/08/02/how-to-use-x-delegates-in-dynamics-ax-2012/

https://en.wikipedia.org/wiki/Observer_pattern


 

Tuesday, November 3, 2015

AX2012 : Tip when using date effective tables as a reference data source

Hi Friends,
Reference data sources is a powerful feature in AX 2012. A reference data source enables you to add the replacement fields for a surrogate foreign key replacement to a form design. It is used extensively on standard AX forms.

It becomes more interesting, when we reference date effective tables . In such scenarios we need to perform some steps to make sure that the date effective tables are queried correctly.

We can find a good example on sales table form. Let us have a look:

1. The date effective data sources are added under the sales table datasoure node as reference data sources.


 
2. The below piece of code on form init() method to check the validity:
 
 
 
3. The below piece of code is added on sales table datasource  >> init() method, to set the valid time state range criterions:
 


If you doing some development task and plan to add date effective tables as reference data source, then make sure to model it the same way. Not doing so will cause an issue that the expired records will display a "Unknown" value in the reference data source record. A similar problem has been reported on Microsoft dynamics AX community forum. Detail can be found at this link. The below image reference is taken from the link:


Whitepaper to use date effective framework can be downloaded from here, however this information is not mentioned there.

Thanks for reading the blog.
 

Monday, September 7, 2015

AX2012 : Adding text translations to entities using X++ [Technical walk-through]

Hi Friends,
Microsoft dynamics AX provides text translation possibilities on some standard entities like products, ledger accounts, financial dimension and few more. Text translations are displayed on documents where a language code is applied for example packing slips and invoices. Also, when the system language for the user corresponds to the translations, the translations are displayed in Enterprise Portal for Microsoft Dynamics AX.


A simple way to store the translations is to open the translations form , select the translation language and then save the translated text. For example to store translations for a product name, open the translations screen from product list page as shown below :



Select the language from the drop down which comes on clicking on the + button


Enter and save the translated text.


There can be instances where you want to extend this capability to other existing or new entities in the system to make your solution flexible and rich. From technical point of there is a standard AX class called SysTransalationHelper which provides capabilities to achieve this. In order to understand how we can use this class, let's do a quick walk-through and extend the standard AX's Questionnaire entity to have ability to set-up text translation.

The standard AX form for questions can be found at Home >> Common >> Questionnaires >> Design >> Questions




PS --> For demonstration purpose, I am just creating the minimum required methods and objects. In real time make sure you follow development best practise recommendations. I will be prefixing new objects with DEM_ to distinguish from standard AX objects.

Step 1 : Create a new table. I called it DEM_KMQuestionTranslation. Now add 2 foreign key relations as shown below, one relation with KMQuestion table and another with LanguageTable. KMQuestion table stores the Questions information and the Language table is used to store list of all languages available on the system. Additionally add a Description field. The table structure should look as shown below:


Add the below 2 methods in this table:

createOrUpdateTransalation() --> This method is used to create or update a question translation record. The implementation is quite straightforward. Refer to standard AX method \Data Dictionary\Tables\EcoResProductTranslation\Methods\createOrUpdateTranslation() to view a similar implementation.




findByQuestionLanguage() --> This method is used to find the specified record in the DEM_KMQuestionTranslation table by using the specified question language. Similar standard AX method implementation can be found at \Data Dictionary\Tables\EcoResProductTranslation\Methods\findByProductLanguage()





Step 2: Now we need to add below line of code in standard AX KMQuestion table insert() method. This is to create a default translation record in our new table whenever a new question record is created in the system.




Step 3 : Now we need to create a new class. Let's call this class DEM_QuestionnairesTranslationHelper  and add couple of methods as shown below:

Class declaration , declare two variables as shown below


Create parm methods to get and set the values





Now here comes the interesting part. Create a method to construct a new object of SysTransalationHelper class and use the tablenum of the main entity table and the new table which is storing the language translation details as shown below


Secondly create a method to launch the translation detail form. To do this we need to create a method as shown below which uses SysTranslationHelper class object as a parameter and calls it’s launch translation form method:


The last method required in this class is to instantiate the class . To do this create a new method as shown below:


Step 4 : Now we create a new menu item for this class



Step 5 : Plug this menu item on the questions form as shown below:


We are done and ready to test drive this. Now on opening the Questions form we can see the transalations button . When we click it,system launches the translation helper form where we can store our translated texts using all standard AX functions as shown below:



You can also download the XPO from  HERE to have quick access to code.

So in this post we saw how we can make our solutions flexible and more intuitive by adding the translation texts capabilities to any existing or new entities.

Thanks for reading the blog.

Tuesday, August 25, 2015

An encounter with Microsoft Dynamics AX Office Add Ins error



Hi Friends,
Recently when configuring options for Microsoft Dynamics AX Office Add Ins, we were getting the below error: "Columns 'OfficeAddinAccountStructureView.ChartOfAccountsId' does not belong to table OfficeAddinAccountStructureView" 



The AOS was moved to a different machine for this client resulting in original installed Microsoft Dynamics AX client configuration to point to wrong AOS.

We tried creating a new client configuration pointing to right AOS/Refresh WCF/Generated full CIL but this also did not helped. So we needed to find a way to fix the AOS name in the default client configuration (which is uneditable from front end).

In order to change the default client configuration, we updated the corresponding registry value for that user. First check the user SID .I my case the SID was \S-1-5-21-861567501-1637723038-839522115-2627, then navigate to the registry editor at the below path:

HKEY_USERS\S-1-5-21-861567501-1637723038-839522115-2627\Software\Microsoft\Dynamics\6.0\Configuration\Original (installed configuration) 







Here we noticed, aos2 key was referring to old AOS. We changed the value of  aos2 key by editing the key. To do this, right click on the AOS2 key and select modify.



Enter the correct AOS, click OK 


Close the registry editor and then open the local client configuration: Notice that the AOS is pointing to correct machine.


After this we were able to configure the Dynamics AX add-ins options and able to carry out our further activities.




So from this encounter we assume* (as we could not find any documentation from Microsoft confirming this), Dynamics AX office Add Ins use default client configuration to first time connect and configure the options. 
Additionally, when we move AOS to different machine, the registry values might still point to old AOS and there can be some situations, like this,where it can cause trouble. 

PS --> Changing registry values is not recommended unless you have proper backup and understanding of how it works. The above approach helped me in the mentioned scenario. In case you are facing the same error then before making any changes in the registry please ensure you have proper backups are place.