Monday, February 6, 2017

Excel import in Dynamics AX 365 or AX7 using Dialog

Excel import in Dynamics AX 365
Now Dynamics AX 365 is running on Web browser so import the data in AX using Excel, CSV, text etc. has been changed. FileName,FilenameOpen extended data type is no more supported to browse the excel file.
If we compare Excel file import process between AX 2012 and Dynamics AX 365 then in AX 2012 file was importing from Client (from local system) to AX database directly but now the new AX is running on web server over the IIS so to import file in AX mean first file need to import on server and need to store in File server or SharePoint or Database. And then read the file from stored location may be from File server, SharePoint or Database.
So how we can import data in AX using Excel file? The simple way is via using Data entities and if data entity does not exist then need to create new Data entity for table and then we can import data via using the excel file or any other supported file using that Data entity.
But sometime required to import the data in AX using dialog in that case Data entities will not help, so in this blog I will describe how we can import data in Dynamics AX 365 using excel file.
In ax 2012 we have used the below classes to import data in AX via using excel file:-
SysExcelApplication application;SysExcelWorkbooks workbooks;SysExcelWorkbook workbook;SysExcelWorksheets worksheets;SysExcelWorksheet worksheet;SysExcelCells cells;
But now in Dynamics AX 365 the above classes does not exist anymore to read excel file.
So how we can import data in AX via Excel file? ,
So first step to import the excel file in AX , create a new class (TestExcelImport) and extend with RunBase and add a new button in dialog with upload caption (Same as AX 2012)
 Object Dialog()
   {
       FormBuildButtonControl buttonControl;
       DialogGroup            dlgGroup;
       FormBuildGroupControl  buttonGroup;
       dialog = super();
              
       dlgGroup       = dialog.addGroup('');
       buttonGroup    = dialog.formBuildDesign().control(dlgGroup.formBuildGroup().id());
       buttonControl  = buttonGroup.addControl(FormControlType::Button, 'Upload');
       buttonControl.text("Upload file");

       buttonControl.registerOverrideMethod(methodStr(FormButtonControl, clicked),
                                        methodStr(TestExcelImport, uploadClickedEvent),
                                        this);
       return dialog;
   }

And below is the upload button click event (click event already registered in above dialog method)
private void uploadClickedEvent(FormButtonControl _formButtonControl)
   {
       FileUploadTemporaryStorageResult result = File::GetFileFromUser() as FileUploadTemporaryStorageResult;
       if (result && result.getUploadStatus())
       {
           result.getFileContentType();
           //result.
           fileUrl = result.getDownloadUrl();
           info(fileUrl);
       }
   }
FileUploadTemporaryStorageResult is the class has been introduced in AX and this class is responsible to browse and upload the file on server (On File server, SharePoint or Database). And from here store the file path (for me fileUrl) in a variable to read the file later in run method.
Now next step to read the data from uploaded excel file:-
 Public  void   run()
   {
       System.Byte[] byteArray;
       System.IO.Stream     stream;
       try
       {
           stream = File::UseFileFromURL(fileUrl);
           this. readExcelData(stream);
           //info("Done"); 
       }
       catch(Exception::Error)
       {
           info(strFmt("%1 %2",Exception::Error,fileUrl));
       }
   }

File class has been used to read excel data from the url (file location from server) and will return the data in stream. Now the task is to read excel Stream data in AX.
So to read stream data of excel file Microsoft have used ExcelPackage (EPPlus ), we can manipulate the file using this package (Create excel,create new worksheet , pivot table , design ,formatting etc.). Developer can access the classes of  EPPlus using OfficeOpenXml namespace. the below classes is responsible to read the data from stream.
       OfficeOpenXml.ExcelWorksheet;
       OfficeOpenXml.ExcelPackage;
And below is the method which is responsible to read the data from stream into container
public container  readExcelData(System.IO.Stream      _stream)
   {
       OfficeOpenXml.ExcelWorksheet _worksheet;
       OfficeOpenXml.ExcelPackage package = new OfficeOpenXml.ExcelPackage(_stream);
       int iRowCount,iCellCount;
       anytype        anyData;
       
       try
       {
           if(package)
           {
               _worksheet = package.get_Workbook().get_Worksheets().Copy("Sheet1","Journal");
               var cells = _worksheet.get_Cells();
               iRowCount = _worksheet.get_Dimension().get_End().get_Row();

               iCellCount = _worksheet.get_Dimension().get_End().get_Column();

               for (int i=2;i<=iRowCount;i++)
               {
                   conRow = conNull();
                   for (int j=1;j<=iCellCount;j++)
                   {
                       anyData= cells.get_Item(i, j).get_Value();
                       if(!anyData && j ==1)
                           break;

                       if(anyData)
                           conRow += anyData;
                       else
                           conRow += "";
                      
                   }
                       if(conRow)
                       {
                           conRow += iRowCount;
                           conData = conIns(conData,i,conRow);
                       }
               
                       
               }
             info(con2Str(conData));
           }
         }
       catch (Exception::CLRError)
       {
           throw error("@SYS135884");
       }
       return conData;
   }
ExcelPackage class is responsible to create  excel package from stream (can be file also in place of Stream), ExcelWorksheet class where the worksheet has been copied with data. And then looping the data of rows from the excel cells.
conData container variable declared in class declaration of class. So now enjoy the excel import in AX.
Note:-Import the data via using CSV or text file still easy and we can use CommaIo or Comma7Io.
For any query kindly let me know.

Monday, August 8, 2016

AX7 Interaction Flow


AX7 Interaction Flow
 
AX 7 Development Overview: - As we know now Dynamics AX 7 development environment is based on Visual Studio and ax now hosted on IIS as a web portal. Developer can implement/customize the functionality using VS and can write the logic/code in x++.

Creating a new Model under existing standard models mean, VS will create a new folder in Application Suite folder with the name of your model name. So let us assume if you are creating a new model with name “MyModel” then VS will create a new folder “MyModel” in Application suite package directory.

 All the standard elements exist in Application suite under the Foundation folder, in Foundation folder there is other separate subfolders for elements. For example AxFoms for forms, AxClasses for Classses for classes, AxTables for Tables etc.

The same sub folders structure (AxForms , AxTables, etc.) also will create in our case for custom models (MyModel)  under MyModel folder. Moreover, if developer has created a new form then the new form will exist in AxForm folder and if developer did customization in any existing standard ax form then the changes related to standard ax form will be exist in “Delta” folder in AxForms folder.

AX7 Web portal interaction flow:-

Microsoft has developed new dynamics AX in two way, one is IIS hosted running website (developed in .net MVC4 using c#) reserved for Microsoft and another second is integrated development environment in visual studio with X++ programming language for Developers.

For dynamics AX developer they do not have to think about the running website on IIS (AosWebApplication). The site developed in .Net MVC4 C#.

 Now question comes how the X++ based developed components communicating with C# based MVC4 AX7 website. Let us see how AX7 website interacting with developed changes.

As we know as an X++ developer, our changes now storing in XMl file and in JSON based file (Forms settings and properties in JSON). All standard elements exist in models and there is three primary standard models by Microsoft 

1.       Application Suite: - Most of the elements exist in these models like AR, AP and other modules forms.

2.       Application Platform: - common forms like dialog, batchRun, syslookup, langaugTable etc.

3.       Application Foundation: - Batchs ,BatchJobs,Documents,KPI etc belongs in this model.

 

Moreover, there is a two way for a developer to develop client’s functionality via extension or over layering.

Therefore, if developer do the changes in existing elements in a new model then the new changes will store in a new xml file in custom model under delta folder or If we are extending existing element then file will create with extension name. Let us assume if we have added one new field in PurchTable then one PurchTable Xml file exist in Application suite model under Foundation >> AxTable folder and if we check the xml file the elements start by root AxTable.

If we have added a new filed in same table then a new xml file with the name PurchTable will create in our custom model under delta folder and if we open that xml file then the root element start with AxTableDelta.

Now if we build our solution via VS then IDE is responsible to synchronize the entire file in package with latest changes and update the required dlls.

Now let us move on AX7 website area of IIS, website developed in MVC4 c#. For .net MVC based website MVC Engine is responsible to handle the request on IIS and the actions of controllers are the roots of access, there are 4 main controller classes

1.       OAuthController :- responsible for authentication of login credential from azure services. This C# class having an action” HandleCode”, this action redirects on a specific url. OAtuth2.0 has been used for authentication. The authentication way is same as OpenId authentication. let us assume, If we develop our own website and want to allow users login via Facebook, Google etc.. Then developer have to register our website in facebook , google developer  api and facebook or google developer api will provide a tenant/applicationid. That id developer have to use in website to authenticate login credential via facebook or google.

However, here in AX7 login credential authentication is done by azure services. Register ax7 domain on azure services, developer have to add ax7 as an application for a domain and user needs to be created under that domain.

 

2.       NavigationSearchController :-  This controller class is responsible to search data on global search textbox (from header) by DoSearch action :-

Moreover, below is the callstack from UI to .Net MVC action. SearchBox.js (JQuery) JS file is responsible for searching from the search text box. An Ajax call will place on search text changed, and sendQueryRequestAsync JS function event will fire on text changed, from this function “doSaerch” action of NavigationSearchController will call, and this action will return data in JSON format to JQuery. Now by JQuery the returned JSON data will bind in HTML and will display on page.

 

Here I have searched “Sales” text and doSearch action ofNavigationSearchController has been called via jquery function and 10 records has been returned and same displaying on page.

Type “Sales”:-

First open development tool of browser by pressing F12 and put debugger on relevant function.



sendQueryRequestAsyn function of SearchBox.js has been called on text changed of search text box


Now “DoSearch” action (In .net MVC the actions of controller is accessible over the internet, same as web service operation) of NavigationSearchController has been called from SearchBox jquery JS. Below is the call at controller end from jquery and here you can see total 10 JSON records returning and on 6th number “Sales Agreements” needs to be display on page


 
In addition, below is the search result on AX7 website:-
 
 


1.       WebContentController :- this controller having two actions one is form and another second is less. Form action need formName  as string parameter this action will get and return forms properties as a JSON data  and later the form will be filled by data on UI.
Let us see the communication flow from UI to ax7 web application 
We can debug JQuery ,Javascript at Browser end, Open your website in any browser and click F12(Browser developer tool) and place breakpoint in JS file where you want by pressing F9 and StepIn use F11 or F10 for Step over.
Below is the important JS file to communicate with server side code in terms of HTML binding: -
Html-binding.js, html-binding-compilation.js, html-binding-serverform.js
Microsoft has created one dyn-core (dyn-core.min-7.0.4030.16079)JQuery library which having default common values but later overriding values from different JS files. For example, html-binding-serverform-metada.js file overriding control’s command and properties (data is in JSON format).
Note in most of the cases there is a single html page (default.htm) in web root, which is responsible to bind the html & data of different form. So default.htm is common form with static html of header and some common html. Below is the main div in which the requested form’s html bind by jQuery.
<div class="primaryConductor" role="main" id="PrimaryConductor" style="display: block;">
            </div>
Actually, whatever form user is trying to access from AX7 website, that form’s HTML is creating dynamically by JQuery and binding on default.htm. At the time of development of elements our changes stored in xml or JSON. When user try to open that form, then stored changes from these files render on default.htm (this html file exist on web root) page.
Press F12 and open developer tool on browser and place a debugger on call function in html-binding.Js now click on Vendor groups or on any existing link in Ax7 website:-


 
Debugger will be there in debugger window and now you can go to the end by pressing F10 or F11


From the below screen we can see a request for http get method from jquery and the path is “webcontent/form/?”formName,  form is an action(form) of WebContentController class.(Note in MVC based site we can access actions over the internet like as web service because in MVC site actions of controller are the events and work like as a communicator between Business logic and Views).

This action form is returning JSON data from business logic to UI.  In addition, if callback successfully return json data at JQuery end then html-binding.js and html-binding-serverform.js and few other JS file will bind the data on UI.

Note: - form action only returning requested forms properties and settings of form not actual data from tables.

And this action just returning JSON file from packages, and for VendGroup form the JSON file exist on the path: - Packages\ApplicationSuite\WebContent\Forms\vendgroup(JSON etension file)   


From the JQuery the request will go to form action of WebContentController class (See below picture) and you can see the debugger hits this class with VendGroup form request.


Action return JSON data to Jquery on UI:-


The success function after success callback:-


Now data is on page with forms properties (Note there is multiple calls for Html, form’s properties and commands, business data etc.): -


Request format by Jquery to calling action of Controller to get JSON data information

accepts: Object
async: true
contentType: "application/x-www-form-urlencoded; charset=UTF-8"
contents: Object
converters: Object
crossDomain: false
dataType: "json"
dataTypes: Array[2]
error: (n,i,r)
flatOptions: Object
global: true
hasContent: false
isLocal: false
jsonp: "callback"
jsonpCallback: ()
processData: true
responseFields: Object
success: (I)
type: "GET"
url: "/webcontent/form/vendtable"
xhr: ()
__proto__: Object
And Response JSON data format :-
"{"Name":"VendTable_form","TypeName":"Form","ValueProperties":{"FormName":"VendTable","Style":"DetailsFormMaster","DisplayParameters":"{} ……….. (More request data)

Reliable Communication manager: - Most of the properties of elements populating by using service operation (ProcessMessages) ReliableCommunicationManager.svc (Excluding JSON based properties of standard from these coming from JSON files above explained). As we know, our changes are now storing in packages under the model in xml format. So this service is responsible to return these xml based file data in JSON format on UI and rendering on page by using JQUERY.

So let us assume if we did the changes in any existing element may be in PurchTable form then in package, there should be two xml artifacts, one is in standard “Application suite” and another in your custom model. And when user will open PurchTable form from AX7 website then code will club all xml file of PurchTable (from standara]d and from custom model) and will return on page via JQuery.
So how page design is rendering on default.htm html, ReliableCommunicationManager service will get the form properties and will return on UI by JQuery and if JSON file also exist for form in webcontent folder then JSON file will return. And all properties will merge and render on UI.


The request format to calling the vendor list form from menu of navigation bar:-
Open Vendors list:=
accepts: Object
async: true
beforeSend: ()
contentType: "application/json; charset=utf-8"
contents: Object
converters: Object
crossDomain: false
data: "{"ChannelId":0,"LastAcknowledgedSequenceNumber":4,"Messages":[{"Interactions":[{"$type":"GetFormInteraction","TargetId":"2","ObjectName":"vendtablelistpage","OpenMethod":0,"ShouldBlockOnExecution":true,"NavigationPath":["mainmenu","AccountsPayable","Vendors"],"PartialDelta":true,"CallbackId":"8"}],"SequenceNumber":8},{"Interactions":[{"$type":"GetMenuStructureInteraction","CommandName":"GetMenuStructureInteraction","TargetId":"ReportMenuItemUsage","ShouldBlockOnExecution":false,"NamedParameters":{"Args1":"VendTableListPage#Display#[\"mainmenu\",\"AccountsPayable\",\"Vendors\",\"VendTableListPage\"]"},"CallbackId":"9"}],"SequenceNumber":9}]}"
dataType: "json"
dataTypes: Array[1]
error: ()
flatOptions: Object
global: true
hasContent: true
isLocal: false
jsonp: "callback"
jsonpCallback: ()
processData: true
responseFields: Object
success: ()
timeout: 600000
type: "POST"
url: "Services/ReliableCommunicationManager.svc/ProcessMessages"
xhr: ()
__proto__: Object
Here is the server request on above web service url 


Now initiating properties and command of form html including grid ( Html-binding-serverform-metadat.js holding all controls properties and commands)



Below all properties & commands of grid has been initiated in $dyn variable


Html-binding-serverform-metadata.js
$dyn.serverForm.metadata.Form:-

 

If anybody have any query please let me know and if I am incorrect anywhere please correct me.