How to Create Exchangerate Provide using .Net in Ax 2012

Follow these steps to create a .NET exchange rate provider.

Install the Microsoft Visual Studio Tools.
Configure the AOS servers for hot swapping. This enables the newly created exchange rate provider to be deployed without requiring a restart of AOS.


Create a new Visual Studio .NET class assembly project. For this example, a C# project was used.
Add a reference to the following assemblies:
1.     The Microsoft.Dynamics.AX.Application.FinancialManagement assembly found in the VSAssemblies folder of the deployed Microsoft Dynamics AX instance—for example,

2.  C:\Program Files\Microsoft Dynamics AX\6.2\Server\AxaptaDev\bin\VSAssemblies. Make sure that the Copy Local property is set to True.
3.   The Microsoft.Dynamics.AX.ManagedInterop assembly found in the bin folder of the deployed Microsoft Dynamics AX instance—for example, C:\Program Files\Microsoft Dynamics AX\6.2\Server\AxaptaDev\bin.

Add the following using statements to the source file where you will define the exchange rate provider.

using Microsoft.Dynamics.AX.Application.FinancialManagement.Currency;
using Microsoft.Dynamics.AX.ManagedInterop;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Xml;

Create an ExchangeRateProviderDotNetOanda class that is derived from the abstract
ExchangeRateProvider base class. 

namespace DotNetExchangeRateProviderOanda
{
    class ExchangeRateProviderDotNetOanda : ExchangeRateProvider
    {
        const string QuoteXPath = "//response/quotes/quote";
        const string BidXPath = "//bid";
        const string DateXPath = "//quote/date";
        const string ServiceTimeoutKey = "serviceTimeout";
        const string ServiceURLKey = "ServiceUrl";
        const string OANDADateFormat = "yyyy-MM-dd";
        const string HttpWebRequestMethod = "GET";
        const string HttpWebRequestContentType = "application/xml";
        const string HttpHeaderAuthorization = "Authorization";
        const string KeyTokenPrefix = "Bearer ";
        private List<decimal> rates;
        private List<DateTime> dates;
    }
}

Add a GuidAttribute attribute to uniquely identify the provider.

[GuidAttribute("FF7BF89C-18C1-42AF-91BA-07BBC3419D39")]
class ExchangeRateProviderDotNetOanda : ExchangeRateProvider
{
        const string QuoteXPath = "//response/quotes/quote";
        const string BidXPath = "//bid";
        const string DateXPath = "//quote/date";
        const string ServiceTimeoutKey = "serviceTimeout";
        const string ServiceURLKey = "ServiceUrl";
        const string OANDADateFormat = "yyyy-MM-dd";
        const string HttpWebRequestMethod = "GET";
        const string HttpWebRequestContentType = "application/xml";
        const string HttpHeaderAuthorization = "Authorization";
        const string KeyTokenPrefix = "Bearer ";
        private List<decimal> rates;
        private List<DateTime> dates;
}

Select to automatically implement the ExchangeRateProvider abstract class.



Implement the Name property. The name used in the following code is hard-coded; however, in production code, a label should be used to enable proper translation. Providers can use the static GetStringFromAxLabel method of the ExchangeRateProvider class to get a label from Microsoft Dynamics AX.

public override string Name
{
    get { return "Oanda .NET provider"; }
}

Implement the Id property. Use the following pattern to return the GUID that is defined in the GuidAttribute attribute of the exchange rate provider.

public override string Id
{
    get { return typeof(ExchangeRateProviderDotNetOanda).GUID.ToString(); }
}

Implement the GetSupportedOptions method. This is the first use of an X++ proxy, the ExchangeRateProviderSupportedOptions class. X++ proxies make it possible to instantiate and use X++ resources and logic within .NET code. Keep in mind that this comes at a performance cost, because all these calls are made through reflection. We recommend that you minimize the number of calls into X++ to increase performance.

Public override ExchangeRateProviderSupportedOptions GetSupportedOptions()
{
    ExchangeRateProviderSupportedOptions supportedOptions = ExchangeRateProviderSupportedOptions.construct();
    supportedOptions.parmDoesSupportSpecificCurrencyPairs(true);
    supportedOptions.parmDoesSupportSpecificDates(true);
    supportedOptions.parmFixedBaseIsoCurrency("");
    return supportedOptions;
}

Implement the GetConfigurationDefaults method.

public override ExchangeRateProviderConfigDefaults GetConfigurationDefaults()
{
    ExchangeRateProviderConfigDefaults configurationDefaults = ExchangeRateProviderConfigDefaults.construct();
    configurationDefaults.addNameValueConfigurationPair("serviceTimeout", "5000");
    configurationDefaults.addNameValueConfigurationPair("serviceUrl", "https://www.oanda.com/rates/api/v1/rates/{0}.xml?quote={1}&start={2}&end={3}&fields=averages");
    return configurationDefaults;
}

Implement the following helper methods. These are used only in this example and are not required for each provider.

private void readRate(string _xmlString)
{
            XmlDocument xmlDom = new System.Xml.XmlDocument();
            XmlNode xmlQuoteNode, xmlBidNode, xmlDateNode;
            decimal exchangeRate;
            System.DateTime exchangeDate;
            string value;
            xmlDom.LoadXml(_xmlString);
            // Find the Quote
            xmlQuoteNode = xmlDom.SelectSingleNode(QuoteXPath);
            if (xmlQuoteNode != null)
            {
                // Find the exchange rate
                xmlBidNode = xmlQuoteNode.SelectSingleNode(BidXPath);
                if (xmlBidNode != null)
                {
                    value = xmlBidNode.InnerText;
                    exchangeRate = Convert.ToDecimal(value);
                    if (exchangeRate != 0)
                    {
                        rates.Add(exchangeRate);
                    }
                }
                //Find the date of the exchange rate.
                xmlDateNode = xmlQuoteNode.SelectSingleNode(DateXPath);
                if (xmlDateNode != null)
                {
                    value = xmlDateNode.InnerText;
                    // convert the date from UTC to local timezone.
                    exchangeDate = System.DateTime.Parse(value, System.Globalization.CultureInfo.CurrentUICulture, System.Globalization.DateTimeStyles.AssumeUniversal);
                    if (exchangeDate != null)
                    {
                        dates.Add(exchangeDate);
                    }
                }
            }
}

Implement the GetExchangeRates method.

public override ExchangeRateResponse GetExchangeRates(ExchangeRateRequest _exchangeRateRequest)
{
    ExchangeRateResponse response = ExchangeRateResponse.construct();
    ExchangeRateResponseCurrencyPair currencyPairResponse;
    ExchangeRateResponseExchangeRate exchangeRateResponse;
    ExchangeRateRequestCurrencyPair currencyPairRequest;
    ExchangeRateProviderConfig config = ExchangeRateProviderConfig.construct();
    string oandaRequestString;
    DateTime currentDate;
    decimal exchangeRate;
    string serviceUrl;
    int serviceTimeout;
    WebResponse webResponse;
    StreamReader streamReader;
    Stream stream;
    HttpWebRequest httpWebRequest;
    WebHeaderCollection webCollection;
    DateTime fromDate, fromUTCDate;
    TimeZone localTimeZone;
    int compareResult;
    string XMLOut;
    string dateForRequest;
    //string key;
    rates = new List<decimal>();
    dates = new List<DateTime>();
    localTimeZone = System.TimeZone.CurrentTimeZone;
    serviceTimeout = Convert.ToInt32(config.getPropertyValue(this.Id, ServiceTimeoutKey));
    serviceUrl = config.getPropertyValue(this.Id, ServiceURLKey);
    // Iterate over the requested currency pairs. This is only required for providers
    // that support specific currency pairs.
    _exchangeRateRequest.initializeCurrencyPairEnumerator();
    while (_exchangeRateRequest.moveNextCurrencyPair())
    {
        // Process each date separately.
        // If we attempt to make a single request for multiple dates
        // then OANDA will average the rates across the dates which is not what we want.
        fromDate = _exchangeRateRequest.parmFromDate();
        compareResult = fromDate.CompareTo(_exchangeRateRequest.parmToDate());
        while (compareResult <= 0)
        {
            currencyPairRequest = _exchangeRateRequest.getCurrentCurrencyPair();
            currencyPairResponse = ExchangeRateResponseCurrencyPair.construct();
            currencyPairResponse.parmFromCurrency(currencyPairRequest.parmFromCurrency());
            currencyPairResponse.parmToCurrency(currencyPairRequest.parmToCurrency());
            // All rates are requested with a display factor of 1 for this provider.
            // If the rates internally are represented using a different exchange rate
            // display factor, the framework will make the necessary adjustments when
            // saving the exchange rates.
                                                                   currencyPairResponse.parmExchangeRateDisplayFactor(ExchangeRateDisplayFactor.One);
            // convert to UTC which is required by OANDA
            fromUTCDate = localTimeZone.ToUniversalTime(fromDate);
            dateForRequest = fromUTCDate.ToString(OANDADateFormat);
            // Build the request URL.
            oandaRequestString = string.Format(serviceUrl,
            currencyPairRequest.parmFromCurrency(),
            currencyPairRequest.parmToCurrency(),
            dateForRequest,
            dateForRequest);
            // Configure the request for OANDA.
            httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(oandaRequestString);
            httpWebRequest.Method = HttpWebRequestMethod;
            httpWebRequest.ContentType = HttpWebRequestContentType;
            httpWebRequest.Timeout = serviceTimeout;
            // Authentication
            webCollection = httpWebRequest.Headers;
            webCollection.Add(HttpHeaderAuthorization, KeyTokenPrefix + //TODO: Retrieve and concatenate your Key from OANDA);
            try
            {
                // Invoke the service
                webResponse = httpWebRequest.GetResponse();
                // Retrieve the XML response.
                stream = webResponse.GetResponseStream();
                streamReader = new System.IO.StreamReader(stream);
                XMLOut = streamReader.ReadToEnd();
                // Parse the XML to retrieve the rate and date.
                this.readRate(XMLOut);
                for (int i = 0; i <= rates.Count - 1; i++)
                {
                    currentDate = dates[i];
                    exchangeRate = rates[i];
                    if (currentDate > DateTime.MinValue && exchangeRate != 0)
                    {
                        exchangeRateResponse = ExchangeRateResponseExchangeRate.construct();
                        exchangeRateResponse.parmValidFrom(currentDate);
                        exchangeRateResponse.parmExchangeRate(exchangeRate);
                        currencyPairResponse.addExchangeRate(exchangeRateResponse);
                        currentDate = DateTime.MinValue;
                        exchangeRate = 0;
                    }
                 }
            }
            catch (WebException e)
            {
                // The service call did not complete. Swallow the exception and try the next
                // currency pair. The framework will be able to determine which currency
                // pairs were successfully retrieved and will display the appropriate
                // warnings to the user.
                string error = e.Message;
            }
            response.addOrUpdateCurrencyPair(currencyPairResponse);
            rates = new List<Decimal>();
            dates = new List<DateTime>();
            fromDate = fromDate.AddDays(1);
            compareResult = fromDate.CompareTo(_exchangeRateRequest.parmToDate());
        }
    }
    return response;
}

Add the new project to the Application Object Tree (AOT).
In the properties of the project, select to deploy the project to the client and the server.
Right-click the project, and then click Deploy. This will deploy the assembly to the server and client so that it is ready to be used by Microsoft Dynamics AX.
The project can now be found under the Visual Studio Projects node of the AOT. It can be opened by right-clicking and then clicking Edit.
Restart the AOS service if the provider is not available when you attempt to register exchange rate providers in the application. Hot swapping should make it unnecessary to do this when you make updates to the assembly; however, when you first add the assembly, a restart may be necessary.
The following classes and methods are important areas of the framework that are handy to understand when you debug issues:

1.   ExchangeRateProviderFactory.initialize() – This method creates instances of the exchange rate providers, and is called when exchange rates are registered or imported. If you cannot get your provider instantiated, start debugging here.
2.   ExchangeRateProviderRegistration.initialize() – This method searches for providers so that they can be registered by the application. If you cannot see your provider in the registration form, start debugging here.
3.   ExchangeRateImportOperation.import() – This method drives the import process, which involves calling the necessary provider and storing the exchange rates.
4.      ExchangeRateProviderConfig – This class provides access to configuration information for the providers.

Related Posts

Previous
Next Post »

Thanks for comments.....