Showing posts with label AX 2012. Show all posts
Showing posts with label AX 2012. Show all posts

Wednesday, June 15, 2016

Using XE Currency Restful API in AX - Using Basic Authentication method



static void XECurrencyConverionRestfulAPI(Args _args)
{
    System.Net.HttpWebRequest           webReq;
    System.Net.HttpWebResponse          webRes;
    CLRObject                           clrObj;
    System.IO.Stream                    stream;
    System.IO.StreamReader              streamRead;
    System.IO.StreamWriter              streamWrite;
    System.Net.ServicePoint             servicePt;
    System.Net.WebHeaderCollection      headers = new System.Net.WebHeaderCollection();
    str                                 userNamePassword, userNamePassword64, ret;
    System.Byte[]                       byte;
    System.Text.Encoding                encoding = System.Text.Encoding::get_UTF8();
 
    //This line gives the user control to run unmanaged and managed code
    new InteropPermission(InteropKind::ClrInterop).assert();
    // Create Object for adding headers
    headers = new System.Net.WebHeaderCollection();
    clrObj = System.Net.WebRequest::Create('https://xecdapi.xe.com/v1/currencies.xml');
    webReq = clrObj;
    //Set Method Type
    webReq.set_Method("GET");
    webReq.set_KeepAlive(true);
    // Set Content type
    webReq.set_ContentType("application/xml");
 
    try
    {
        userNamePassword = 'YOURUSERNAME:YOURPASSWORD';
        //64 bit encode user name and password
        byte = encoding.GetBytes(userNamePassword);
        userNamePassword64 = System.Convert::ToBase64String(byte);
        headers.Add("Authorization", 'Basic ' + userNamePassword64);
 
        //Add header to request
        webReq.set_Headers(headers);
        //Gets the response object
        webRes = webReq.GetResponse();
    }
    catch (Exception::CLRError)
    {
        //Access the last CLR Exception
        info(CLRInterop::getLastException().ToString());
     
        //See AifUtil::getClrErrorMessage for another alternative
        //how to parse the Exception object
    }
    //Get Stream
    stream = webRes.GetResponseStream();
    streamRead = new System.IO.StreamReader(stream);
    ret = streamRead.ReadToEnd();
    stream.Close();
    webRes.Close();
}

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

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. 

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

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();
}


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.

Wednesday, December 31, 2014

Applying Current User Id range in an AOT query

If you want to apply a range to an AX 2012 AOT query, you can simply add a new range to the field and specify the integer or string value against them. However, if you want to apply a range on a User Id field and want to provide the value of Currently logged in user, you need to get the value dynamically and pass that value to the query. Below snap shot shows a very simple way to achieve this.


Tips on Extensible Data Security in AX 2012

Recently, I had chance to work on Extensible Data Security and noticed few things which can be very helpful, they are posted below.

  1. XDS can hurt performance sometimes if query is not designed well. In order to design security without hurting the performance, always try to reduce number of joins in your Security Policy Query.
  2. If your requirement is complex and needs to add some dynamic filters to the query. In that case you can create a temporary table with type TEMPDB and override its xds() method to fill the table dynamically. This method can be considered for performance improvements as it would reduce query joins. For this, please see xds method on the table MyLegalEntitiesForXDS
  3. If you have to disable XDS, consider following 2 methods
    1. Create a view and fetch data from that view. View is a different object for XDS framework, thus the security applied on the table will not be considered for view and you will see the data.
    2. Second method to disable the security is mentioned here in my other post.

AX 2012 Disable/ByPass extensible data security (XDS) through code

Extensible Data Security or XDS is a framework introduced in AX 2012 to apply security on data. We had RLS or Record Level Security in AX 2009. However, XDS is completely a new framework with more capabilities in order to apply data security. This post is about disabling or bypassing the XDS using code. For more details about XDS, download a whitepaper provided by Microsoft using following link.
http://www.microsoft.com/en-us/download/details.aspx?id=3110

Disabling/Bypassing XDS or Extensible Data Security:
Recently, I had a requirement to restrict users from viewing all the Warehouses available and they must be able to see only the Warehouses they belong to. I designed a new security policy and it started working very easily without much problem. However, the problem was on Inventory Transfer From where we have two fields for Warehouses:
  1. From warehouse and
  2. To warehouse
Our requirement was to restrict only From warehouse and To warehouse MUST show all the warehouses available so they can issue inventory to any warehouse but should not be allowed to issue inventory from the warehouse they don't belong to. In order to achieve this, I had to disable the XDS on To Warehouse lookup method. I did override the lookup() method on To Warehouse field on the datasource and below is the code which I wrote to disable the XDS.

public void lookup(FormControl _formControl, str _filterStr)
{
    XDSServices                      xXDS = new XDSServices();

    xXDS.setXDSState(0); // Disable XDS
    super(_formControl, _filterStr);
    xXDS.setXDSState(1); // Enable XDS
}

Wednesday, December 17, 2014

AX 2012 showing lookup from different (cross) company

I had a requirement in AX 2012 in which I had to show Customer Lookup from a different company. Our requirement was to provide user with two lookups on the Vendor Form (VendTable).

  1. One lookup - To select the company from which they want to see Customers
  2. Second lookup - Show Customers from the company selected on above lookup
To achieve this I added 2 fields in the table VendTable
  1. TargetCompanyId (EDT: SelectableDataAreaId)
  2. TargetCustAccount (EDT: CustVendAC) - Don't use CustAccount EDT, I tried this in AX2009 and it appeared to be using EDT relation and could not work.
After adding these fields, create a new relationship and add both of the above added fields in this relationship.

Drop the fields on the form and you will see Customer will appear from the company you have selected in the first lookup. Change company from the first lookup and see how data in the customer lookup changes.


Tuesday, November 26, 2013

AX 2012 how to get new number using Number Sequence

Question:

How to get new number in AX 2012 using Number Sequence

Answer:

NumberSeq num;
num = NumberSeq::newGetNum(RetailParametersEx2::numSLInternalRequisitionNumber());

AX 2012 Get Current Worker Id

Question:
How to get Current Worker Id in Dynamics AX 2012

Answer:
HcmWorkerRecId workerRecId = DirPersonUser::currentWorker();

Thursday, November 21, 2013

Debugging Enterprise Portal code in AX 2009/2012

File open dialog on AX 2012 form

Problem:
I want to show a file open dialog on AX 2012 form but when I click the open dialog button it doesn't respond.

Solution:
On Runbase OR Sys operations framework, all you have to do is set the EDT to FileNameOpen and dialog will appear, however, on AX form only setting the EDT will not do. You will need to add following methods to your form methods node.

str filenameLookupFileName()
{
    return "";
}

FilenameFilter filenameLookupFilter()
{
    return ["@SYS134052", #AllFilesName+#AllFilesExt];
}

str filenameLookupInitialPath()
{
    return "";
}

str filenameLookupTitle()
{
    return "Select a trade agreement to import";
}

Tuesday, June 18, 2013

AX 2012 AIF cannot set field value from .net application

I was working with one of the AIF document services in Dynamics AX 2012. I was trying to create general journal record from .net application but I noticed that few of my field values were not getting saved in AX. AIF will not throw any error, records were getting created but few fields were missing values even I provided the values.

For example:
ledgerJournalTrans.AmountCurCredit  = Convert.ToInt64(500.00);
           
I was trying to set the Credit value like above but I could not. I then found that we can set the specified attribute for such fields to true and then AIF will consider to set the values for these fields. See example below.

ledgerJournalTrans.AmountCurCreditSpecified = true;

When I added this statement, everything started to work as expected.

Sunday, June 16, 2013

Understanding types of AIF services in AX 2012

Dynamics AX 2012 offers different type of services that can be used to perform several operations. It is very important to understand which type of service should be used to perform your desired operation.

There are 3 types of services that are available in Microsoft Dynamics AX 2012.


  1. Document Services
    • Document services are services which expose a business entity. For example, Customer, Vendor, Employee, Sales Order.
    • You can Map the business entity using Query and run through Document Service Wizard to build the web service and perform CRUD operations.
  2. Custom Services
    • To expose a custom x++ logic. 
    • Say, you have a custom module and you want to expose some logic/code to your custom application.
  3. System Services
    • System services are kind of utility services.
    • Up and running when AOS starts.
    • Can be used for interactive clients 
    • Build Ad-hoc query

Wednesday, June 5, 2013

AX 2012 Email templates

While performing several operations in Dynamics AX 2012. We often need to send emails to the users that includes some dynamic information that needs to be included in the email or we can say we need to generate email body on the fly. For example, if I need to send email to several worker, I would like to change the worker name for every email.

Dynamics AX 2012 offers email templates for that purpose, we can use variables in email templates and then swap their values at runtime while sending emails.

First of all, you will need to define an email template. To do this go to Organization Administration à Setup à Email templates.

Once the email templates form is open, create a new email template and give it a name of your choice and fill other fields like Sender email, Sender name, default language etc in both the grids. On the bottom grid, select HTML as Layout.

After the template is created, click on Email message button, this will give you an editor window where you can compose your message. See a sample message below.

Dear %WorkerName%
You have been assigned a project, please find the details below
Event Id: %ProjectId%
Event Name: %ProjectName%


Regards.

Notice the variables defined with % sign, they will be replaced with appropriate values using the following code.

Go to the event from which you want to send the email, for example a button click event and then write following code.

SysEmailId sysEmailId = ProjParameters::find('EmailTemplateName');
Map mappings;
str recepient;
HcmWorker worker;
ProjTable projTable;

worker = HcmWorker::find(_worker);
projTable = projTable::find(_projId);

if (worker && projTable)
{
        recepient = worker.email();

        mappings = new Map(Types::String,Types::String);
        mappings.insert("WorkerName", worker.name()); // this will replace variable with actual value.
        mappings.insert("ProjectId", projTable.ProjId);
        mappings.insert("ProjectName", projTable.Name);

        SysEmailTable::sendMail(sysEmailId,
                        SysEmailTable::find(sysEmailId).DefaultLanguage,
                        recepient,
                        mappings,
                        "",
                        "",
                        true,
                        curUserId(),
                        true);
    }

That's it!!! your email will be sent with appropriate values using the mappings defined above.


Tuesday, June 4, 2013

Execute custom code when a record is inserted using AIF in AX 2012

Dynamics AX 2012 offers a very easy to use wizard to generate AIF services that can be used to perform CRUD operations from any other third party app.

We often need to perform some other operations that needs to have some business logic written to perform some operations.

For the explaination purpose, we will take the example of Sales Order.

One requirement could be to post the Sales Order automatically to AX once it is created in the system. Dynamics AX 2012 provides us the service that can create sales order. However, if you also need to post the Sales Order when it is created using AIF, you will need to write some custom code that can perform this operation for you whenever a Sales Order is created.

The method that will be called after inserting record is the updateNow() method of the Axd<Table> class and for Sales Order, it would be updateNow method of AxdSalesOrder class to perform our operation to post the Sales Order.

We can create our own method in the same class or some other helper class and then call that custom method from the updateNow method to get things done.

See the sample code:
public void updateNow()
{
    // call your custom method here
    AxdSalesOrderHelper::postSalesOrderConfirmation(salesTable);
   
}