Microsoft Dynamics AX Support

This blog contains posts by the Microsoft Dynamics AX Support teams Worldwide

AX for Retail 2012: Customizing the Transaction Service

AX for Retail 2012: Customizing the Transaction Service

Rate This
  • Comments 9

 

A very frequent request in AX for Retail 2009 was for the ability to customize the Retail Transaction Service.  The Transaction Service is used to make synchronous calls into AX (Retail Headquarters) to get real-time information – a very common scenario for many POS operations.

While it was possible to customize Transaction Service in 2009, it was a bit of a hack and wasn’t officially supported.  I’m happy to report that this functionality was added in 2012 and it is now very easy to create your own methods in AX that you can call from the POS.

[Update, April 24, 2012]  After writing this I chatted with one of our developers about this new functionality.  While what I wrote will still work, the preferred way that you should extend the Transaction Service is by using the RetailTransactionServiceEx class in AX instead of adding your method to the RetailTransactionService class.  When calling your method, you should then use the TransactionServices.InvokeExtension method instead of the Invoke method.  Also, your read-only collection will be a zero-based array instead of one-based (which is a good thing).  The code samples have been updated accordingly.

Just as a brush-up, a quick overview on how the POS and Transaction Service work together:  The Transaction Service is a standard Windows Service that listens to the POS on a specific TCP/IP port – it’s only job is to listen for requests (almost exclusively from the POS) and turn those requests over to the AX .Net Business Connector for processing on the AOS.  Since this is done synchronously, it holds up the POS until a processing is returned from the AOS.  A message is returned which will contain not only a success or failure flag, but usually some sort of data (a payout) that is needed by the POS.  The Transaction Service doesn’t actually do any work – it’s just a “middle man” between the POS and the AX Headquarters.

As mentioned, the Transaction Service places calls into AX using the .Net Business Connector.  In the previous version, any possible calls were hard-coded directly in the Transaction Service executable.  In AX for Retail 2012 we have added a way to send in a generic method name for execution.  All you need to do is add this method to the RetailTransactionServiceEx class in the AOT and you can immediately start calling it from the POS.

The X++ Side

Here is a method that you can copy and paste into your RetailTransactionServiceEx class:

public static container myTestMethod(str TestString)
{
   
container       returnResult;
   
;

   
if (timeNow() mod 2 == 0)
   
{
       
returnResult = [true,"", strFmt("Current second (%1) was even.  You sent:  %2", timeNow(),TestString)];
   
}
   
else
       
returnResult = [false, strFmt("This call failed.  The current second (%1) was odd instead of even.", timeNow()), ""];

   
return returnResult;

}

There are a couple of things of interest in this code.  First of all, the method has to be a static method – the calls are made through the .Net Business Connector without actually creating an instance of the RetailTransactionServiceEx object.  Secondly, there is only one option for returning information back to your POS code:  an X++ container object.  In this example, I have created a container of three values:  a boolean for success or failure, an error message, and a payout.  The simple code just uses the current time (odd or even second) to determine success or failure.  The important thing with sending back a container is to make sure that your call is expecting the same number of values. 

The final thing to note on the X++ side are the parameters that are sent in.  This is a simple array of objects; my code just sends in one (a string).  Because X++ is very closely-related to all .Net languages, arguments between C# and X++ are usually pretty interchangeable, but I would caution against getting too creative.  Stick to more simple types (strings, integers) and if you need to send in more structured data, convert it to XML.  You can see some good examples in the methods in the RetailTransactionService class.

The POS (C#) Side

An easy way to test out your new method is to add the following code to your Blank Operation plug-in:

using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
 
{…}
 
        public void BlankOperation(IBlankOperationInfo operationInfo, IPosTransaction posTransaction)
       
{

           
try
           
{
               
ReadOnlyCollection<object> containerArray;
               
containerArray = Application.TransactionServices.InvokeExtension("myTestMethod", "ThisTest");

               
bool retValue = (bool)containerArray[0];
               
string errorMessage = containerArray[1].ToString();
               
string payout = containerArray[2].ToString();
               
string comment = string.Empty;

               
if (retValue)
               
{
                   
comment = string.Format("Call succeeded.  Payout:\n{0}", payout);
               
}
               
else
               
{
                   
comment = string.Format("Call failed.  Error Message:\n{0}", errorMessage);
               
}

               
using (LSRetailPosis.POSProcesses.frmMessage dialog = new LSRetailPosis.POSProcesses.frmMessage(comment.ToString(), MessageBoxButtons.OK, MessageBoxIcon.Error))
               
{
                   
LSRetailPosis.POSProcesses.POSFormsManager.ShowPOSForm(dialog);
               
}

           
}
           
catch (System.Exception ex)
           
{
               
LSRetailPosis.ApplicationExceptionHandler.HandleException(this.ToString(), ex);
               
throw;
           
}
       
}

As you can see, to call your method you just use the Application.TransactionServices.Invoke()  method with your X++ method name and your parameter(s).  The X++ container gets returned as a ReadOnlyCollection that you can use to read the results.

Note the distinction between an error in the business logic (the boolean in the containerArray) and the try/catch block.  An exception in the latter will most commonly happen if the Transaction Service is down but can also happen if your method is not found in the RetailTransactionService class or if there are some type conversion problems.  As a developer, you’ll have to make sure to line up method names and data types in both your POS code and the X++ code; because they are separate environments, there is no automatic checking to help you out.

Final Notes

Most of the calls to Transaction Service from the POS code still use the hard-coded methods, but some have been migrated over to using the and Invoke() method.  Take a look at the SalesOrder.cs file in the Sales Order plug-in for some good examples of how you can get creative for packaging up information to send back and forth from C# to X++ code.  The corresponding X++ methods in the RetailTransactionService are especially worth digging into to see how columns and rows of data are created to send back.

I was very happy to see this new functionality added to the product – hopefully it will make your life a bit easier as you’re customizing the POS.

Leave a Comment
  • Please add 5 and 8 and type the answer here:
  • Post
  • Hi Shane

    Nice article and I can see it is fairly simply to customize and add new functionality to the Retail Transaction Service (RTS). However I was wondering whether it is possible to use the RTS with other POS systems and if so have you any examples where it has been done?

    /Erik

  • I have had that question come up and I've taken a look at the architecture of RTS a little bit.  The calls to the Transaction Service are all done through the POS through a couple of DLLs in the POS directory.  It also uses the POS table (Transaction Service Profile) for authentication.  I really don't think it would be possible to actually use RTS directly from another app.

    However, keep in mind that the RTS really is just a "middle man" to the X++ classes in AX.  To create similar functionality, I would consider just using traditional AX 2012 Services (web services) if you want to expose logic to another POS app.

  • Hi Shane, excuseme please, my english is poor, I need help for this topic please, the problem is that, I created a method in RetailTransactionServiceEx Class for invoke from c# but the new changes in this class not work, the question is: How To Replicate the new changes in the ax class (RetailTransactionServiceEx) for use in c# from POS???

    Thanks by your Help

    From Ecuador.

  • Edgar,

    For some reason (mainly related to the .Net Business Connector) the X++ code is sometimes cached and it is difficult to force it to take the new code.

    The best way that I've found to force it to take the new code is to restart the Windows Process Activation Service in your local services on your Real-Time Service machine.

  • Excellent!!! Thank You for you important help Shane, good Job, the problem is resolved

    Thanks from Ecuador

  • Hi Shane,

    I am just a beginner in POS customization. I have tried it many time but always got this exception "RetailTransactionServiceEx object does not have method <method_name>"

    Could u have any ideas for this? (I try to work it on AX R3).

    Thank you.

  • Hi Shane,

    I have tried it many times, but always got this exception "RetailTransactionServiceEx object does not have method <method_name>". Could you have any ideas about this? Did i miss any setup?

    Thank you.

  • QuanLe,  what version of AX for Retail are you working with?  Feature Pack, R2 or R3?  I don't believe things have changed in this area through the various releases, but it's possible.  One thing to make sure of is to create the X++ method, compile, generate CIL, restart the AOS, and potentially restart the Windows Process Activation service (as mentioned in another comment).

  • Quan Le,

    Just realized you mentioned R3 in your other post.  I did run through this exact example in R3 EPOS and things still work as expected.  I did notice that somewhere along the line we switched back to storing the payload in [1], [2], [3] of the container instead of [0], [1], [2] so that tripped me up.  I will update the code sample again.

    If you continue to have issues on this feel free to open a support case and we can see if we can help you out.

Page 1 of 1 (9 items)