Microsoft Dynamics AX Support

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

AX for Retail: Customizing the Retail Transaction Service

[Update: April 18, 2012 – This ability to customize the Transaction Service has been greatly enhanced in AX for Retail 2012.  Please see this article for information.]

A common request that we get from customers wanting to extend the AX for Retail is how to customize the Retail Transaction Service to send custom data back and forth between the POS and Headquarters.  While the Retail Transaction Service is not designed to be customizable, there is a way to piggy-back on existing functionality to extend its functionality.  While this is not a supported method, it’s very powerful.

Before digging into the details, a little background on the Retail Transaction Service.

All communication between the POS and Headquarters (The Dynamics AX Application Object Server or AOS) is handled by the Retail Transaction Service (RTS).  Examples of this communication of calls from the POS to Headquarters might be adding a new customer, querying a gift card balance, and adding points to a customer’s loyalty card.  Anything that needs to be handled from a central location (HQ) in real time goes through the Transaction Service.

If you were to dig into the RTS, you would see that the architecture is relatively simple.  RTS is a standard Windows Service and is configured to listen on a specified TCP/IP port.  All communication from each POS comes through standard TCP/IP calls (no domain authentication necessary!).  In turn, the RTS communicates to the Dynamics AX AOS via the standard .Net Business Connector.  This communication is handled like any other AX Business Connector client.

The RTS itself pretty much just handles the traffic.  It has a pre-defined set of methods that can be called by the POS.  Each of these methods then uses the Business Connector to call a matching method in the PosIsTransactionService class in AX.  This is done synchronously, so the POS waits until RTS returns a result (success or failure) along with any data associated with it.

The interesting thing about the calls from the POS to the AOS (via the RTS) [yikes] is in how data is passed back and forth.  In Dynamics AX 2009 there isn’t a great way to match up AX objects with .Net objects.  This means that a Sales Order object (for instance) might get converted to a large delimited string in the POS before getting passed to AX.  AX would then parse out this string and populate whatever objects it needs with the data.

This is a lot of information to answer a simple question:  how can I customize the RTS?

As mentioned, RTS is not truly customizable.  There is a pre-defined set of the methods that POS uses and you cannot add your own.  However, as is the case with all X++ code in AX, you can modify the code on the AX side of things.  And there are two RTS methods used by the POS to handle inventory transactions, each of which could be modified to handle different types of data and different business needs.

The two methods are ExportStoreInventoryData (get information from AX) and ImportStoreInventoryData (send information to AX).  These are multi-purpose methods, meaning one parameter (“cmd”) is used to tell the X++ code which branch of logic to take.  The basic idea is to add your own “cmd” parameter, call one of these methods with a specific string representing data, and then create new logic in X++ to handle that data.

Each of these commands use the same parameters:
•    succeeded (bool, reference variable): Whether the call to Transaction Service was successful
•    comment (string, reference variable):  The main “payout” of the call to the Transaction Service. 
•    cmd (string):  The command that Terminal Services will execute on the variable passed in.
•    hhtId (string):  The Terminal ID of the POS client making the call.  This can be found in the ApplicationSettings.Terminal.TerminalId application variable.
•    Entrytype (String):  First variable that can be sent to the command.
•    DocType (String): Second variable that can be sent to the command.

When calling either of these commands, you can use the parameters in the following manner: One parameter to identify the command (cmd), one parameter returned to note whether the call was succesful (succeeded), two input parameters (Entrytype and DocType), and one output parameter containing any data returned (comment).

Note that since the input parameters and main output parameter are string variables, you may need to be creative in passing data back and forth. Since you can do whatever you want with the string in the X++ code, you can use any logic to convert data to string and back to data again. For example, if you have multiple columns and rows of data, you can send the data as an XML string or as a tab-delimited set of values with a newline separating lines.

To create your own command (“cmd” parameter), you modify the following methods the HHTStoreInventoryService class in the AOT: ExportHHTData() and ImportHHTData(). Simply add a new case to the switch statement (this is the ExportHHTData method):

        case #HHT_STOCKCOUNT:
output = fileWriter.writeStockCount(HHTHandheldSetup::find(hhtId));
output = “This is the output from mycommand.”;

To test your new method, use the following code (C#) in your POS plugin to call the Retail Transaction Service:

            bool succeeded = false;
string returnstring = string.Empty;
string cmd = “MYCOMMAND”;
string terminal = LSRetailPosis.Settings.ApplicationSettings.Terminal.TerminalId;
string string1 = string.Empty;
string string2 = string.Empty;
LSRetailPosis.TransactionServices.ExportStoreInventoryData(ref succeeded, ref returnstring, cmd, terminal, string1, string2);
if (succeeded)
//Do something with the “returnstring” variable
//Return an error to the user that Transaction Service call failed
As previously mentioned, there is quite a bit of work involved in the details of getting this to work, and it’s not supported, but as it the case with all customizations, once you have a way to do something, as a developer you can support this along with your other customizations.