Microsoft Dynamics 365 Blog

The Transportation Management (TMS) module that shipped with Microsoft Dynamics AX 2012 R3 includes a number of engines that are essentially implementations of strategies for calculating rates, distance, transit time, and other data related to transportation of goods. Some of the most wanted implementations retrieve data from online services. Due to release policy management, it was not possible to ship these engines – even as examples – therefore none of these are included in Microsoft Dynamics Ax 2012 R3. In one of the earlier blogs I described how to create a mileage engine based on Bing Maps. In this blog I will describe a few more.

Part of the solution that we acquired contained rate engines that retrieve rates from the following service providers:

  • FedEx
  • UPS
  • ConnectShip Toolkit (through Progistics AMP)

Additionally, the solution included a mileage engine that retrieves mileage data from the PCMiler system.

These engines won’t be available in an upcoming release, so to allow our partners to leverage existing work, we’re making them available to the partner community through PartnerSource. The source code can be downloaded for you to test to determine if it suits your needs. Please be aware that the code is not in production state, and will require customizations in order to work. In this blog post I will explain the steps that are needed for installing, debugging, and making the code ready for testing. Moreover, I will also explain how to retrieve and record the shipping labels directly from the provider (UPS) without the need to synthetize the label structure locally in AX.

Please follow the link below to download the source code from PartnerSource:

https://mbs.microsoft.com/partnersource/northamerica/support/hot-topics/msdax2012R3transtmgmteng

Disclaimer

  • These private projects are not an officially supported fix from Microsoft. This is an as-is proposal intended for customer test purposes only.
  • Customers must deploy and test these private projects in their test own environment. These private projects should never be deployed in a customer’s production environment.
  • Customers must ensure that these private projects and related code be removed from their test environment after testing is finalized.
  • Microsoft is not liable for any consequential damage from the deployment of any of these private projects.

Installing the initial third-party integration code to AX

In this section I’ll describe how to retrieve the sources of the engines for integrating with the specific services that were mentioned earlier. We will make sure that the code is compiled and available for utilization by the AOS process.

  1. Ensure that your AOS configuration allows debugging and enables hot-swapping of assemblies for each developer session.
  2. Download the third-party engines sources from PartnerSource.
  3. Unzip the sources in your working folder.
  4. Ensure that you have Visual Studio tools for Microsoft Dynamics AX installed (depending on your upgrade state you may need Visual Studio 2010 or Visual Studio 2013).
  5. Start Visual Studio and open the solution file for TMS.ThirdParty.Parcel (located under [working folder]\C Sharp Projects\TMS.ThirdParty.Parcel\TMS.ThirdParty.Parcel.sln). After completing this step, in your Solution Explorer you will see the following 5 projects.
    Note that the projects are originally developed in Visual Studio 2010. If you’re using Visual Studio 2013, the solution will be upgraded.
  6. In your solution you will notice that for each of the projects there are project-to-project references to Microsoft.Dynamics.AX.Tms. These are broken because the referenced project is missing in the solution.
    In order to fix this, you need to add the missing project from the Application Explorer. To do this, follow these steps

    • In Application Explorer navigate to the following project:
      \Visual Studio Projects\C Sharp Projects\Microsoft.Dynamics.AX.Tms
    • Open the context menu and choose Edit.
    • The project is now added to your solution, together with the 5 other projects. You can see that the references are now fixed.
  7. On each of the projects, set the Deploy to Server property to Yes.
  8. Build the solution. Be sure that you do not have any build errors.
  9. Add following projects to your solution in the AOT. Call Add <project name> to AOT from the context menu in Solution Explorer:
    • TMS.ThirdParty.Parcel
    • TMS.ThirdParty.Parcel.Fedex
    • TMS.ThirdParty.Parcel.Progistics
    • TMS.ThirdParty.Parcel.Ups
    • TMS.ThirdParty.Parcel.PCMiler
  10. Deploy the solution to the AOS. Select Deploy solution from the context menu on the solution node in Solution Explorer.
    When you start the AX client and open AOT, you will see that the following projects, together with updated project outputs (containing dll’s) are in the AOT under \Visual Studio Projects\C Sharp Projects).

Enabling the new engines in Microsoft Dynamics AX

In this section we’ll go through the steps needed to enable the new engines in AX, so that they can be utilized in the Rate shopping scenario (and other related scenarios as well). The goal here is to bring the environment to the state in which you will be able to start debugging and developing the missing pieces of code to make the engines fully operational.

  1. Open the Rate engine form (Transportation management > Setup > Engines > Rate engine) and create a record for each of the rate engine classes that you installed.
    1. The Engine assembly should have the full name (with extension) of the dll file specified for each of the rate engine projects.
    2. The Engine type should contain the fully-qualified name with the namespace of the class that implements IRateEngine

      You can create the following records for the 3 rating engines deployed in the system.Rate engine: ThirdParty.Ups
      Rate base type: <empty>
      Name: UPS online
      Engine assembly: Tms.ThirdParty.Parcel.Ups.dll
      Engine type: Tms.ThirdParty.Parcel.Ups.UpsWSRateEngine

      Rate engine: ThirdParty.Progistic
      Rate base type: <empty>
      Name: Progistics online
      Engine assembly: Tms.ThirdParty.Parcel.Progistics.dll
      Engine type: Tms.ThirdParty.Parcel.Progistics.ProgisticsRateEngine

      Rate engine: ThirdParty.FedEx
      Rate base type: <empty>
      Name: FedEx online
      Engine assembly: Tms.ThirdParty.Parcel.Fedex.dll
      Engine type: Tms.ThirdParty.Parcel.Fedex.FedexWSRateEngine

  2. For each of the rate engines created in previous step you need to define RateWSUrl, ShipWSUrl and VoidWSUrl parameters. Each of these parameters defines the URL, which points to the service endpoint specific to particular carrier operation (Rate, Ship or Void). You should be able to find the proper URLs on the particular carrier (for instance UPS) web site. In Rate engine form, for each of the engines open the Engine parameter form, by clicking the Parameters button. See the following screenshot for reference (you need to replace the values in the Parameter value column with appropriate URLs):
  3. Open the Mileage engine form (Transportation management > Setup > Engines > Mileage engine) and create a record for the PCMiler mileage engine:
    • The Engine assembly should have the full name (with extension) of the dll file specified for the PCMiler project, which is Tms.ThirdParty.PCMiler.dll.
    • The Engine type should contain the fully-qualified name with the namespace of the class that implements IMileageEngine, in particular Tms.ThirdParty.PCMiler.PCMilerMileageEngine.
  4. Similarly as for the 3 rate engines defined before, you can define parameters that are specified on the Engine parameters form. This form can be accessed from the form that is used for defining the engines themselves. Similarly as for all the other engine types, these parameters are defined as key-value, and passed to Initialize methods on engine classes, upon engine object initialization. It’s your responsibility to clear up the initialization code and add/remove any parameters that make sense or don’t make sense in engine class initialization.
  5. Now, let’s create a carrier that utilizes one of the new rating engines (Transportation management > Setup > General > Shipping carrier). For this example enable UPS engine. To do this, create a new carrier with one valid service record and one rating profile. The engine does not require any data from AX for rate calculation, therefore you do not need to associate any Rate master with your rating profile. Your carrier setup should look like the following screenshot.
  6. You can also enable the Mileage engine for consumption. If you initialized the TMS module (by clicking the Initialize base engine data button on the Transportation management parameters form), you’ll find the P2PMileage rate engine in the list of the rate engines defined on the Rate engine form. Out of the box that engine uses the P2P mileage engine. For the sake of testing, you can swap the P2P engine with the PCMiler engine by modifying the MileageEngineCode parameter. The value of the parameter should be the value of the Mileage engine key specified when defining the PCMiler engine in step 3. In this case it was P2PMileage, and the setup should look like this.
  7. You should now be able to exercise the engines that you just enabled, and start the debugging session. Attach debugger to ax32serv.exe process. Set a breakpoint on the first line of the Initialize method on UpsWSRateEngine class.
    If you execute the Rate shopping operation, you should be able to hit the breakpoint. To do that, open the Rate route workbench form from a Sales Order and click the Rate shop button.

Updating the source code of the UPS engine

If you let the debugging session continue, you will end up with exception being thrown inside the UPSService class. The web request cannot be processed because a few things are missing. In this section I’ll explain the parts that we know are broken for the UPS engine and need to be fixed by you in order to get things working. Don’t panic. The changes are less complicated than it may seem. My advice is to fix one thing at the time and repeat the debugging session after each completed step. After you are able to retrieve rates, you may choose to close a container and see if you can make the Ship operation work (see the next paragraph). This section concentrates on the UPS engine, but a similar approach should be taken for completing the integration with the FedEx engine, and the two other remaining engines.

  1. Authentication data needs to be provided. After obtaining the account for accessing the web services on the UPS website, you’ll receive an account number and access key. You will also note the user name and the password needed to access the service. If you look a little bit further in the Rate method of UPSService, you will find the following block of code.
    Here, we are passing the security related data, which is needed to authenticate the request to the UPS web service. In the current state of the sources, the SecurityInfo object is not initialized. You will need to ensure that proper security data is provided. In your development exercise you may simply hardcode the authentication data. However in the final solution it should probably be loaded from the UI/secret store. You can implement your security data assignment in the SeedSecurityData method on the BaseParcelRatingEngine class. Remember to threat model this part of the system when you design your final solution.
  2. You are now able to step through the actual request. Your first response from the UPS system will fail because you may be missing some basic information, such as phone numbers. The response object will contain the developer friendly error data, which you can use to complete a valid rating request.

Closing a container and printing shipping labels

If you set the Activate carrier rating flag on the shipping carrier, you will enable the special type or rating processing that results in calling the Ship method on your parcel service. After packing your goods on the Pack form (Warehouse management > Common > Pack), upon closing the container you will call the TMS managed system to update the rates and ship the package. In addition to assigning the tracking number, one of the most important outcomes of this call is the retrieval of the shipping label data. Out of the box the code is configured to retrieve the data in ZPL format. If your bar code printer can interpret this format correctly, you will be able to print the shipping labels recorded directly from the carrier service, without building their structure. In the AddPackageCharges method of the BaseParcelRatingEngine C# class, you will find a line of code that packages label data retrieved from the service into the XML response of the TMS managed system.
This data is interpreted by the TMSProcessXML_Container X++ class. In this class you will find a number of methods that record data based on the XML response from the TMS managed system. In particular you will find a readShipContainer method, which at some point updates the tracking numbers in the WHSContainerTable table. This is the point at which you should consider additional customization of X++ code:

  1. Add a ShipLabel field to WHSContainerTable, of type string and set the StringSize to (Memo).
  2. Assign the ShipLabel field to WHSContainerTable when processing data in \Classes\TMSProcessXML_Container\readShipContainer method. You can use following line of code to retrieve the label data:
    this.returnNamedNode(rateNode, ‘LabelData’);
  3. Add a button on the Containers form to print the labels. You can call the following static server method to print labels:
    \Classes\TMSCommProxy\printLabel
    The first parameter identifies the printer available to AOS, the second parameter is the value of the ShipLabel table field on WHSContainerTable that was recorded earlier.
  4. You may consider further modifications that will let you print the labels automatically when closing containers.

Conclusions

We’ve gone through some basic steps needed to initiate the development work related to the third-party carrier integration for TMS. We’ve also looked into the UPS integration. I will not go through the other engines in detail, but they should require similar conceptual work in order to get enabled.

The code that you are working with is not exactly in a plug-n-play state. It requires some developer skills in order to execute properly. However, we worked with a pilot team that followed the implementation process in this blog and within a few days they were able to enable the UPS rating with shipping label printing. We believe that similar effort is required to get the FedEx integration to work.

We have not had have time to assess the state and quality of ConnectShip and PCMiler integration. We are however including the sources as well, so that you can work on incorporating these into your systems if desired.

When working with the code, remember the following tips:

  • If you see that you cannot hit the breakpoint, it may mean that your assemblies on the AOS are out of sync with the assemblies (and pdb files) in the build output folder in VS. In that case, you need to re-deploy the solution and restart AOS.
  • Threat model your solution. Remember that URLs and security data need to be treated as secured resources.
  • Always ensure that when debugging you are using test access to the actual web services. You definitely do not want to see a big UPS truck waiting in front of your software development offices the day after your debugging session, simply because you stepped over their service call a couple of times, while executing this tutorial.

We're always looking for feedback and would like to hear from you. Please head to the Dynamics 365 Community to start a discussion, ask questions, and tell us what you think!