Third-party Transportation Management (TMS) engines (UPS, FedEx, Progistics, PCMiler)


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/data base. 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.
Comments (33)

  1. Pooja says:

    It is a very good topic about TMS engines, I tried to follow the whole process but when I clicked on the Rate Shop I was getting this error message below

    " Error RateShopBroker, TMSException"

    Can you please help ?

  2. Pawel Kruk says:

    Hi Pooja,

    Did you try to first ensure that you can hit breakpoint on "Init" method of the engine class? If you do not hit breakpoint there, you may have some issue with setup data.

    You can see more details about the problem that occurs on your environment in TMS error log (Transportation management>Periodic>Transportation system error log). Here you will find the exception details with the error message that may help you out with find out what is broken.

    Pawel

  3. PoojaSaini says:

    Hi Pawel,

    Yes, I did hit the breakpoint on the engine class method

    Below are the details of the issue:-

    Message: Error RateShopBroker, TMSException

    Source: Microsoft.Dynamics.Ax.Tms

    Stack trace:   at Microsoft.Dynamics.Ax.Tms.API.TmsService.ExecuteTransaction(String requestXml)

    Exception at level 1

    Message: Value cannot be null.

    Parameter name: uriString

    Source: System

    Stack trace:   at System.Uri..ctor(String uriString)

      at Microsoft.Dynamics.Ax.Tms.Data.RateEngineParameters.get_RateWSUrl()

      at Tms.ThirdParty.Parcel.BaseParcelRatingEngine.SeedRateRequest(XElement shipment)

      at Tms.ThirdParty.Parcel.Ups.UpsWSRateEngine.PerformRating(TransactionFacade transactionFacade, IList`1 packages, XElement shipment)

      at Tms.ThirdParty.Parcel.Ups.UpsWSRateEngine.Rate(TransactionFacade transactionFacade, XElement shipment, String rateMasterCode)

      at Microsoft.Dynamics.Ax.Tms.Bll.RatingService.ProcessRating(TransactionFacade transactionFacade, XElement segment, RatingDto ratingDto)

      at Microsoft.Dynamics.Ax.Tms.Bll.RatingService.RateEntity(TransactionFacade transactionFacade, XElement entity, IList`1 ratingDtos)

      at Microsoft.Dynamics.Ax.Tms.Bll.RateShopBroker.RateEntity(TransactionFacade transactionFacade, XElement entity)

  4. Pawel Kruk says:

    Hi Pooja,

    Looks like you found a bug in my tutorial. My apologies. The problem is that

    current implementation does not allow situation in which the service URLs are not specified as parameters on the engines (although the code with "overwrite ws URL" suggests its possible). In order to fix the problem, you should do following:

    1. Find your rate engine in "Rate engine" form.

    2. Click "Parameters"

    3. Specify following 3 parameters:

    Name: RateWSUrl

    Value: The URL to UPS rate service endopoint

    Name: ShipWSUrl

    Value: The URL to UPS ship service endopoint

    Name: VoidWSUrl

    Value: The URL to UPS void service endopoint

    With that, you may revert any changes you did to re-assignment of WebServiceUrl property on your request objects in UpsService class.

    I will update the blog post to reflect this.

    Pawel

  5. Pawel Kruk says:

    I updated the blog post based on Pooja's comment. I added 2nd step in "Enabling the new engines in Microsoft Dynamics AX" paragraph and removed the step about customizing the URL specific code in the "Updating the source code of the UPS engine" paragraph.

    Pawel

  6. PoojaSaini says:

    Hi Pawel,

    Thank you for the reply. I was think the same that we may need to specify URL for specified engines. I will get the url's and try again. Thanks.

    Pooja

  7. Pooja Saini says:

    So if we want to just create a mileage engine then we can just go with the PC Miler mileage engine…….or Do we required to setup Rate Engine as well ??

  8. Pawel Kruk says:

    Hi Pooja,

    If you are only interested in PC Miler engine, you do not need to include any of the rating engines attached  in discussed PartnerSource solution. You can use PC Miler with one of the rating engines available out of the box in AX, for example Microsoft.Dynamics.Ax.Tms.Bll.MileageRateEngine.

    You can read a bit on how to associate PC Miler engine with rating engine in point #6 of "Enabling the new engines in Microsoft Dynamics AX" paragraph within this post.

    You may also check out the blog post i published earlier, about creating a mileage engine based on BingMaps:

    blogs.msdn.com/…/transportation-management-tms-mileage-engine-based-on-bing-maps.aspx

    I hope it helps,

    Pawel

  9. Pooja Saini says:

    Thanks Pawel….It's very helpful

    Pooja

  10. Pooja says:

    Hi Pawel,

    I tried to create same engine Bing Map Mileage engine, i got a temporary key from bing maps.

    But i after Enabling the engine and clicking on Rate Route workbench –> Rate Shop

    i was not able to get the miles as shown above in your blog with the same address from and to, after processing i was able to get lines with the exceptions in the exception column, i am not able to add snapshot shots for you to view. But can you help if you understood my issue ?

    And if we want to have bing map or pc miler or any other like ups service we need to buy the subscription from the company …. and get a weblink ? right ?

  11. Pawel Kruk says:

    Hi Pooja,

    I posted a small guidance on the other blog post, pointing you to where to start debugging.

    As for the second question, correct. Typically if you wish to commercially consume the 3rd party mileage service providers, you will quite likely require a paid subscription. I'm not sure what's the licensing on PC Miler, but for BingMaps you will require an Enterprise key. You can read more about it here:

    http://www.microsoft.com/…/create-a-bing-maps-key.aspx

    Pawel

  12. Michelle says:

    Has anyone had problems getting the parameters to work with PC Miler? I tried adding in the parameter code in the Initialization method but I get the error "The type or namespace name 'MileageEngineParameters' could not be found (are you missing a using directive or an assembly reference?)"

  13. Pawel Kruk says:

    Hi Michelle,

    Sorry for delayed answer. I guess what you refer to, is compile error you are getting when you un-comment the code in the Initialize method. This is because the MileageEngineParameters class is not defined at all within your project. This class is very easy to define. You should take a look at RateEngineParameters class as an analogy to understand how to do that. What you are trying to construct, is a class that has a number of methods (currently actually defined as getters for public properties) that retrieve values of parameters based on predefined keys. As example on how to define, see how the MileageEngineCode property is defined on RateEngineParameters class. It uses RetrieveStringValue method for retrieving parameter value based on specific parameter name (key). The hardcoded parameter name (key) is what you will use for setting up parameters in "Parameters" forms for various engines in TMS module.

    Hope it helps if you didn't solve this by now.

    Pawel

  14. Ajit says:

    Hi Powel,

    What should we write in "seedSecurityData"  method of class BaseParcelRatingEngine class? We have done with all setups but we are not getting response, progress bar runs for some seconds and then blank.

    Thanks in advance.

  15. Pawel Kruk says:

    Hi Ajit,

    The BaseParcelRatingEngine class has a property called SecurityData of type SecurityInfo. In SeedSecurityData method you need to initialize this object. The following properties of this object should be assigned in this method:

    -AccessKey

    -User

    -Password

    -AccountNumber

    All of these are used by UPS and FedEx, but in order to fully understand how each of these 4 properties is used, please inspect the actual code of the rate engine you are trying to enable. You will then see that for instance in the UPS case the account number corresponds to the shipper number:

    shipper.ShipperNumber = request.SecurityInfo.AccountNumber;

  16. raj says:

    hi pawel,

    how to integrate ups website to ax …

  17. Pawel Kruk says:

    Hi Raj,

    Can you please tell me more specifically what you mean by integrating UPS website to AX?

    Pawel

  18. Andrew says:

    Hi Pawel,

    If you're still around answering questions, I have an issue where I have deployed the rate engine projects to the server, I attached visual studio to the AX32Serv process, I put a breakpoint in my code, then go to the rate workbench, fill out the details then click "rate shop" but it doesn't hit my breakpoint.

    They remain red outlines with the warning "No symbols have been loaded into this document".  I'm positive the correct DLL is in the server bin folder that AX must be looking at.  Do you have an idea what might be causing this.  I get that many of the "symbols" are loaded in at runtime but I see no reason why even the initialize method won't get hit.

    Thank you

  19. aksrauf says:

    Hi Pawel,

    You have mentioned that this code project is not production ready. Is there any code released by MSFT which we should take as a baseline to use in any production deployment?

    We were able to build the integration successfully, however, we want to be able to deploy it in production environment as well.

    Thanks.

  20. Pawel Kruk says:

    Hi aksrauf,

    My big apologies for the late answer. We are not planning to include this code into the production code base, so you will need to productize and maintain these engines on your own.

    Pawel

  21. Pawel Kruk says:

    Hi Andrew,

    Big apologies for late answer to you too. I guess you may have solved the debugging problem already.

    But just to clarify:

    The tricky part with Visual Studio integration and debugging is that whenever you hit deploy, an assembly is saved as part of your project output in AOT, but not necessarily loaded into AOS app domain you use for testing.

    When you restart AOS, your assembly will be replaced in the AOS bin with the version in your AOT. That said, if you wish to debug, you may need to rebuild, re-deploy your project, restart AOS, and then attach to AxServ32. Please notice that after these steps, any recompile will result in replacing dll and the pdb files in your project folder. In that case they will not match the AOS dll, and you need to go through  the project re-deployment again.

    With these steps, if you still don't see the symbols loaded, please try to open Modules window in VS, find your assembly and select "load symbols" from the context menu. If this doesn't help, I would suggest double checking that the dll in the server bin is exactly the same as in project bindebug folder. Please remember that if you call "deploy" from another AX layer than your project, the project output in AOT may not be updated.

    Pawel

  22. Chris says:

    Hi Pawel,

    The 3rd party engines for FedEx and UPS and providing us with rate, labels and tracking numbers :). However, I am not seeing an transit time information being displayed. (because there is no transit time engine setup on the rating profile). Would another 3rd party transit time engine be required to get this infomraiton?

    1. Joe_Gramann says:

      Hello, is there still someone out there who can help on this topic. We are on AX2012 R3 CU11 and really struggle to get the above blog working.
      On the rate shop work bench we get a return with and exception that says TODO; Specify code

      on closing a container we get the TMS ShipBroker. TMSException.

      We are really stuck and any advise on where we can turn to would be highly appreciated

      Thanks

  23. Pawel Kruk says:

    Hi Chris,

    Can you verify that the FedEx or UPS service you are calling into actually provides transit time in the response? If yes, you may want to consider developing support for transit time within your rating engine, or build a new transit time engine.

    Otherwise, you can set up one of the existing transit time engines to retrieve the transit time.

  24. Gowtham says:

    Hi Pawel,

    i have following your blog for PC Miler integrate with AX. I have struggled on the  Rate engine form parameter setting(RateWSUrl,ShipWSUrl,VoidWSUrl) value. For PC Miler what parameter value i need to set?

    Thanks in Advance.

    Regards,

    Gowtham

  25. Derek says:

    Hi Pawal,

    Any chance these will be updated for AX7 when it is released?

    Thanks,

    Derek

  26. Michael says:

    Hi Pawal,

    Please will make these accessible for customers to download from CustomerSource.  

    Thanks,

    Michael

  27. Vaidas Karosas says:

    Hi Gowtham,

    The specific endpoints are not public information, from what I can see, and might be different for different customers of PC*MILER. You might need to contact PC*MILER to find this out.

    Vaidas

  28. Vaidas Karosas says:

    Hi Derek,

    We have recently published a more generic tutorial on creating third-party TMS engines on the new AX: ax.help.dynamics.com/…/create-a-new-transportation-management-engine.

    Vaidas

  29. Vaidas Karosas says:

    Hi Michael,

    I assume you mean source code mentioned in this post. As mentioned in the post itself, this source code is not ready for a customer's production environment, and would require customization and testing before deployment. Therefore, PartnerSource is a better place to keep it.

    Vaidas

  30. Once after the solution is loaded the project Microsoft.Dynamics.Ax.Tms is added to the solution, then upon Building the project you might get the following error:

    The type or namespace name ‘Ax’ does not exist in the namespace ‘Microsoft.Dynamics’ (are you missing an assembly reference?)

    Why?
    Because, namespace Ax doesnt exist in namespace Microsoft.Dynamics

    For real? (Might not be)
    Double check by navigating to namespace (using go to definition). If you can see the definitions, then its all good.

    Restart VS and the issue should be resolved.

  31. I am unable to process packages with a weight >150 LBs. UPS & FEDEX API(s) both return me a message “The maximum per package weight for the selected service from the selected country is 150.0” (Hard:111035)

    Does anyone know the workaround?

    1. Mark W says:

      Packages more than 150 pounds must be shipped as a “freight” shipment, rather than a package shipment.

Skip to main content