Wednesday, December 16, 2015

No matching MessageFilter was found for the given Message

We are calling an AIF web service from a .Net API. Everything was working and suddenly after a model store deployment, we were unable to use that service and were getting following error.

No matching MessageFilter was found for the given Message

I tried everything and could not find anything wrong. Web.config, WCF bindings everything was OK but still it would not work.

After spending couple of hours, I removed the Service References (not just update, remoed and re-added) from my code, rebuilt the API and published on IIS again and it started working.

Sunday, December 6, 2015

Sales order confirmation in batch - adding extended logic

The out of the box Sales order confirmation functionality allows us to either confirm Sales order using a click of a button (on Sales order form/list page) or we can schedule the confirmation of Sales order using a batch job (it also supports late selection query). This all works fine. However, if you want to perform additional tasks on each Sales order once it is confirmed, you will need to write some code. The class structure is something like that

FormLetterService
SalesFormLetter
SalesFormLetter_Confirmation

Any developer familiar to inheritance would go and override the run method of SalesFormLetter_Confirmation class to perform the additional updates after the super call and it will work perfect when a Sales order is confirmed using a button. However, it will NOT work as expected when runs under a batch. It is because, batch creates multi threaded tasks and leaves them to execute later. The code written in run method of SalesFormLetter_Confirmation class would run before the Sales order is actually confirmed (under different thread) and you will not achieve your desired results as code would not be execute for SOs being confirmed under a batch.

To overcome this issue, the code must be executed when the thread actually confirms the Sales order. After performing some debugging using Visual Studio and analysing the threads I found few ways to achieve the desired results and execute the custom code for each and every Sales order under the batch right after it gets Confirmed.


  1. There is a class called "FormLetterServiceBatchTaskManager". It has run method - write your code there after the base class' run gets executed. Better check the this.ParmDocumentStatus to execute your code only for the document type you are working on. (if this.ParmDocumentStatus() == DocumentStatus::Confirmation)
  2. Write your code in FormLetterService.run() method after journal gets created (Again, check the document status and execute your code only for your desired document type). 
There are some other classes which actually creates journal and they could also be considered for performing similar tasks based on your requirements.
  1. FormLetterJournalCreate (Parent class - following are few of its child classes)
  2. SalesConfirmJournalCreate
  3. SalesPickingListJournalCreate etc.

Dynamics AX Load Balancing tips

Working with Single AOS environment and Load balanced environment is very different. Specially, when it comes to releasing code in production environment or making changes in AIF service data policies. Following are few things which I learnt in my recent project:


  1. If running multiple AOSs for load balancing and you need to make data policy changes (i-e need to enable or disable fields in a web service), you must bring all AOSs down and restart the service when change is made and CIL is performed.
  2. If running multiple AOSs and made some code change that needs CIL change. Again, must bring all AOSs down and restart them when change is made to make the change effective in all environments (servers)
  3. If running multiple AOSs and modified workflow (i-e a new workflow version is added), Must refresh cache in all other environments. You don't need to bring production environment down for that purpose and just login to all servers and refresh the cache (Tools > Cache > Refresh AOD, data, dictionary)
  4. If you are using load balancing and you have written custom .net API which calls AIF services. The custom .Net API MUST be deployed on physical cluster and not the load balanced one. Otherwise, it won't be able to authenticate and you will get NTLM authentication error.

I will keep updating this post as I find and learn more. 

Tuesday, October 27, 2015

Start Visual Studio in CUS, VAR layer

If you are developing for EP and creating/modifying web controls. You would notice that changes you make in Visual Studio will be published in USR layer. To start visual studio in some other layer, create a Visual Studio shortcut > Right Click > Properties and set target to something like this:

"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\devenv.exe" /AxConfig  "C:\AXCU8_DEV10_VAR.axc"

The highlighted text is basically pointing to the AX configuration file for VAR, CUS layer.

Saturday, August 15, 2015

AX 7 - New Features

Recently, I came across a white paper providing summary of new features that have been implemented in AX7.  The paper needed partner source access so I am providing highlights here for those who don't have access to partner source. Please note that the White paper is draft and subject to change.

AX  NEW FEATURES:
FOUNDATION
What can you do?
AX 2012
‘AX 7’
Access the client, anytime, anywhere
The AX 2012 desktop client provided a full set of forms, but was limited to running on Windows machines and required installation. Terminal Server was often used with the desktop client to enable access over a WAN. The Enterprise Portal web client provided a reduced set of forms.
The two AX 2012 clients have been replaced by a single, standards based web client that provides the full set of functionality of the desktop client with the reach of the Enterprise Portal client.



FINANCIALS MANAGEMENT
What can you do?
AX 2012
‘AX 7’
Export account structures to Excel

Not supported
You can now select an account structure and export that to Excel.
View ledgers and advanced rule structures associated with an account structure on a single form
In AX 2012, the user has to navigate to multiple forms to see the ledger that the account structure is using.
Fact boxes have been added to the account structure form.
Filter Management Reporter (labeled Financial reporting in ‘AX 7’) reports based on dimension, attributes, dates, and scenarios
All filtering of Management Reporter reports was handled through the design of the report. If a user with viewing privileges wanted to view a report for a different date, for example, this required a report designer to make the modification
Report options have been added so when viewing a report, different filters can be applied and a new report is generated based on those filters.
View Management Reporter (financial reports) within the ‘AX 7’ client
A separate web client was used for viewing Management Reporter reports.
A list of all financial reports can be accessed within the ‘AX 7’ client. The user selects a report to view, and the report is rendered within the ‘AX 7’ client.
Monitor budget vs actuals and create ledger forecasts using the Ledger budgets and forecasts workspace and additional inquiry forms
Not available
The workspace can be accessed through the ‘AX 7’ dashboard. It includes links to a number of new inquiry forms: Actuals vs budget summary, Budget control statistic summary, Budget register entries, Budget plans.
Create layouts for budget plans and forecasts
The Budget plan document is viewed as a list of lines with effective dates and amounts for financial dimension combinations. The user must create and use Excel templates to view budget plan data in a pivot.
An unlimited number of layouts is available for budget plans and forecasts. You can combine selected financial dimensions, user defined columns, and other row attributes (such as comments, projects, assets, etc.) in the layout. Users can switch the layout for the budget plan document on the fly and edit data using any selected layout. Budget planning configuration is simplified by eliminating scenario constraints and using layouts to define which data can be viewed and edited in each budget plan document stage.
Print the Vendor Invoice Transactions report with information from the Detailed Due Day List which includes the days past due.
In AX 2012, you had to print two different reports: the “Detailed Due Day List” and the “Vendor Invoice Transactions” report.
The information contained on the two reports were consolidated onto the “Vendor Invoice Transactions” report. The Detailed Due Day List was deprecated.


HUMAN CAPITAL MANAGEMENT
What can you do?
AX 2012
‘AX 7’
Transfer skills and certificates to class participants upon course completion
Manual process in AX 2012.
Upon completion of a course a new option will be available to update participant’s records with the new skills and certificates.
Quick employment verification capability
Manual process in AX 2012
Accessed from a workspace or the employee form HR can quickly verify employment.
Enable employees to view, update, and delete information from the system
Available in AX 2012 with limited view and update capability.
This feature is enabled for employees and contractors to view a wide range of personal data with optional workflow when creating new, updating existing, or deleting information from the system.
Enable managers to view or edit employee information
Available in AX 2012 with limited view and update capability.
Managers are empowered based on configuration settings and security to view or edit employee information.
Encrypt ID numbers
Not Available
All Employee ID (SSN) numbers are encrypted.
Access compensation processing results
Available only at time of processing
Compensation processing results can now be accessed at any point after the process has run.
Access benefit processing results
Available only at time of processing
Benefits processing results can now be accessed at any point after the process has run.
View Date Effective timeline changes
Not Available
This comparison tool is available for Employees, Positions, and Jobs. It provides a comprehensive view of changes from one version of a record to another.
View employees by company
Manual process in AX 2012 through filtering
Employee and Contractor lists are automatically filtered by the company you’re logged into.
Update course participants list
Not Available
Course participants can be removed from the participants list.
Manage compensation events in mass
Not Available
This feature provides a streamlined processing of compensation changes for employees.


Wednesday, August 12, 2015

AX 2012 Get Item purchase price using x++

Question: I need to get Item purchasing price using x++ code.

Answer:

container result;

result = PriceDisc::findItemPriceAgreement(ModuleInventPurchSales::Purch, ItemId, InventDim, Unit, today(), Qty, VendAccountNum, CurrencyCode, "0");

purchPrice            = conPeek(result, 0);

Note: The variables shown above to pass as parameters are self explanatory and should be replaced with your variables.

Monday, August 10, 2015

Redirect page to some other page when dialog closed on EP

Recently, we had a requirement to open a dialog page from list page and when user closes the dialog, we had to redirect our page to some other page. I know it can be done very easily by handling the close dialog event handler. However, we are talking about list page here. Where we don't have option to write c# code. This is where the web development skills comes into play and can help you achieve some fast results where you don't have much time to modify sharepoint web parts etc.

I was able to accomplish this using Javascript. I registered the closeWindow handler when the dialog was loaded, built the URL for the EP menu item using c# code and registered the script. Whenever the dialog would close, a javascript function would execute to redirect the page. Remember, in my case, the requirement was to redirect the page but if you have some other requirement when closing the dialog, you can achieve that as well. Sample code:

protected void Page_Load(object sender, EventArgs e)
    {
        String linkOneURL;
        String clientScriptName = "CloseWindow";
        Type clientScriptType = this.GetType();
        ClientScriptManager cs = Page.ClientScript;
        System.Text.StringBuilder javaScript = new System.Text.StringBuilder();
        AxUrlMenuItem shoppingCartPageMenuItem = new AxUrlMenuItem("EPCSSSalesBasket");
        String shoppingCartUrl = shoppingCartPageMenuItem.Url.OriginalString;
        String buildUrl = String.Empty;

        if (!IsPostBack)
        {
        
            linkOneURL = this.dsCustTable.GetDataSet().DataSetRun.AxaptaObjectAdapter.Call("BuildLinkOneURL").ToString();
            this.LinkOneFrame.Attributes.Add("src", linkOneURL); // I am loading some third party page here under IFrame.

            if (!cs.IsStartupScriptRegistered(clientScriptType, clientScriptName))
            {
                buildUrl = "window.frameElement.navigateParent('" + shoppingCartUrl + "');"; // This is the URL i want to redirect.


                javaScript.Append("<script type='text/javascript'>");
                javaScript.Append("function UnLoadWindow() {");
                javaScript.Append(buildUrl);
                javaScript.Append("}");
                javaScript.Append("window.onbeforeunload = UnLoadWindow;");
                javaScript.Append("</script>");

                cs.RegisterStartupScript(clientScriptType, clientScriptName, javaScript.ToString()); // Registering the Javascript function - this would be executed when the dialog is closed.

            }
           
        }
    }

AX 2012 showing third party website in EP/Sharepoint dialog

We recently had a requirement to show a third party website in our EP site. The requirement was to show the website in Enterprise Portal dialog and URL was not static. We had to build URL at runtime by using fields from the selected record. As you know, we cannot write code on EP list page (we can use interaction class but we cannot write c# code for list page), so options were very limited. We added a Menu item button on List page and passed the selected record as context to that button. The button was pointing to a new web control (or sharepoint page) and we kept this page blank. All it had was an IFrame. See sample HTML below.

<dynamics:AxDataSource ID="dsCustTable" runat="server" DataSetName="dsCustTable" ProviderView="CustTable"></dynamics:AxDataSource>
<div style="height: 100%">  
    <iframe id="LinkOneFrame" runat="server" height="800" width="100%" ></iframe>
</div>

Now, we need to load our page in that IFrame. First thing was to get the URL as it is not a static URL but we need to build that on fly. We created a static method on table to build that using x++ and returned the URL from x++.

Following is the c# code to get the URL and load the page in URL.

linkOneURL = this.dsCustTable.GetDataSet().DataSetRun.AxaptaObjectAdapter.Call("BuildLinkOneURL").ToString();

this.LinkOneFrame.Attributes.Add("src", linkOneURL);

When you click the button on list page a new popup dialog would open and it would show the page in Iframe.

Sunday, May 10, 2015

AX2012 AIF Document Services - Include fields which are not part of AOT query

While exporting data from AX to other third party systems. There could be a requirement to send data which is not part of your AOT query. There could be a requirement to include data from other tables which are not part of your query OR cannot become part of query because of complex joins or may be they need some custom business logic which cannot be achieved using Query. You can always include such fields by creating parm methods for these fields in AX<tableName> classes. For example:

public str parmWBS(str _wbs = '')
{
    GeneralJournalEntry     generalJournalEntry;
    ProjInvoiceJour         projInvoiceJour;

    if (this.parmText() == '300')
    {
        select generalJournalEntry
            where generalJournalEntry.RecId == GeneralJournalAccountEntry.GeneralJournalEntry
            && generalJournalEntry.JournalCategory == LedgerTransType::Project
        join projInvoiceJour
            where projInvoiceJour.LedgerVoucher == generalJournalEntry.SubledgerVoucher;

        if (projInvoiceJour)
        {
            _wbs = projInvoiceJour.ProjInvoiceProjId;
        }
    }

    return _wbs;
}

In above method, based on the existing parmText() field on Query, I am trying to fetch the Project Invoice Id from the table which is not part of the query.

Once, parm method is written, you will need to:


  1. Run the Update Document Service Wizar
  2. Open AIFService forms, filter the service you are working on and click Refresh
  3. Generate Incremental IL

AX 2012 upload file to FTP with batch processing support

One of a very common requirements for any ERP systems is to have FTP file upload support to upload files from AX to FTP server. We do have many libraries, tools available to do that. However, it is best to have native X++ code without referencing any DLLs to upload your files to FTP from AX. Following code shows the example of uploading files to FTP.

The code written below can be easily used to upload files, However, if you have to process this as a batch you need to consider that batch runs on server/CIL and for this you need to set permissions and also if that still doesn't work, you can change the method to static server.

private server static void sendToFTP()
{
    System.Object ftpo;
    System.Object ftpResponse;
    System.Net.FtpWebRequest request;
    System.IO.StreamReader reader;
    System.IO.Stream requestStream;
    System.Byte[] bytes;
    
    System.Text.Encoding utf8;
    System.Net.FtpWebResponse response;
    System.Object credential;
    HOSInventParameters hosInventParameters;

    int             fileCount;
    System.Array    files;
    int             i;
    str             nextFile;
    Filename        filepath;
    Filename        fileType,fileNameString;
    FileNameType    pattern = '*.TXT';
    FileName        ftpFileName,fileName;


    select firstOnly custInventParameters;
    new InteropPermission(InteropKind::ClrInterop).assert();



    files           = System.IO.Directory::GetFiles(custInventParameters.PartsFileLocation, pattern);

    if (files)
    {
        fileCount =    files.get_Length();

        for(i=0; i < fileCount; i++)
        {
            nextFile    = files.GetValue(i);

            [filepath, filename, fileType] = fileNameSplit(nextFile);
            fileNameString= filename + fileType;
            ftpFileName = custInventParameters.PartsFTPAddress + '/'custInventParameters.PartsFTPLocation + '/' + fileNameString;

            reader = new System.IO.StreamReader(nextFile);
            utf8 = System.Text.Encoding::get_UTF8();
            bytes = utf8.GetBytes( reader.ReadToEnd() );
            reader.Close();
            // little workaround to get around the casting in .NET
            ftpo = System.Net.WebRequest::Create(ftpFileName);
            request = ftpo;

            credential = new System.Net.NetworkCredential(custInventParameters.PartsUserId,custInventParameters.PartsFTPPassword);
            request.set_Credentials(credential);
            request.set_ContentLength(bytes.get_Length());
            request.set_Method('STOR');
            // "Bypass" a HTTP Proxy (FTP transfer through a proxy causes an exception)
            // request.set_Proxy( System.Net.GlobalProxySelection::GetEmptyWebProxy() );
            requestStream = request.GetRequestStream();
            requestStream.Write(bytes,0,bytes.get_Length());
            requestStream.Close();

            ftpResponse = request.GetResponse();
            response = ftpResponse;
            info(response.get_StatusDescription());
        }
    }

    CodeAccessPermission::revertAssert();
}


Monday, April 6, 2015

Calling RESTful services using AX 2009, AX 2012


While integrating Dynamics AX with other systems, you might need to call REST services in Dynamics AX to Pull data in AX or Push data out of AX to other systems. REST services are the easiest and very useful way to provide the interface between 2 systems. The Job written below is the example of how one can call REST service from within AX.

Tip: Either you want to push data from AX or pull data in AX, you can always use POST method. In case you want to send data out of AX, you will always need to use POST method with your data as shown below in postData variable. In case, you need to download data in AX from some other system, you can still use POST method and send your credentials, API key for authentication purpose and download your data either in XML or JSON format as I have done in returnValue variable in job written below.

static void Job1(Args _args)
{
    str                             url;
    str                             postData;
    str                             returnValue;
    System.Net.HttpWebRequest       request;
    System.Net.HttpWebResponse      response;
    System.Byte[]                   byteArray;
    System.IO.Stream                dataStream;
    System.IO.StreamReader          streamReader;
    System.Net.ServicePoint         servicePoint;
    System.Net.ServicePointManager  servicePointManager;
    CLRObject                       clrObj;
    System.Text.Encoding            utf8;

    ;

    postData = strfmt('api_key=%1&account_id=%2', "DEC8A896-9244-4FAB-ACB9-15B932E630CB", "3");

    new InteropPermission(InteropKind::ClrInterop).assert();

    url = "http://yoururl.com/services/api/external/authentication.ashx?login";

    clrObj = System.Net.WebRequest::Create(url);
    System.Net.ServicePointManager::set_Expect100Continue(false);

    request = clrObj;
    request.set_Method("POST");
    utf8 = System.Text.Encoding::get_UTF8();
    byteArray = utf8.GetBytes(postData);
    request.set_ContentType("application/x-www-form-urlencoded");
    request.set_ContentLength(byteArray.get_Length());

    dataStream = request.GetRequestStream();
    dataStream.Write(byteArray, 0, byteArray.get_Length());
    dataStream.Close();

    try
    {
        response = request.GetResponse();
    }
    catch (Exception::CLRError)
    {
    postdata = "";
    }

    dataStream = response.GetResponseStream();
    streamReader = new System.IO.StreamReader(dataStream);
    returnValue = streamReader.ReadToEnd();

    info(returnvalue);

    streamReader.Close();
    dataStream.Close();
    response.Close();


}

Thursday, February 12, 2015

Easiest say to obtain SID - AX 2012 installation

We often need to obtain SID while installing or moving Dynamics AX across different environments. There are several ways to obtain SID which we need to set in the database table. However, the easiest way to obtain SID is to:

Open command prompt and type the following:

whoami /user

That's it.