Code highlighting

Showing posts with label label layout. Show all posts
Showing posts with label label layout. Show all posts

Sunday, October 02, 2016

Tutorial: Extending the label printing functionality in Microsoft Dynamics AX 7

Today I would like to shed a bit more light onto how to extend the label printing functionality, specifically, show how to add a new field from the work line to be displayed on the label.

This is something we have created as a demo for Tech Conf a while back, and you can still see the recording where Per and Zach from our team showcases the different label printing scenarios.
You can view the different Microsoft conference videos here.

Just as a refresher, here is a guide for how to set up label printing in Dynamics AX 7:
http://kashperuk.blogspot.dk/2016/10/tutorial-label-printing-in-microsoft.html

Technical introduction

The label printing "framework" consists of three main components:

  • WHSLicensePlateLabel table, which contains the information for the label, which is substituted into the label through the variables.
  • WHSLicensePlateLabelBuild class, which is responsible for populating the WHSLicensePlateLabel table with data for a particular work order (line).
  • WHSDocumentRouting class, which is responsible for the actual printing of the label, as well as the substitution of variable values, using the below methods:
    • initMenuFields() method is responsible for building the list of substitute fields based on the WHSLicensePlateLabel table fields
    • printDocument() method finds the specific document routing record that matches the flow criteria (e.g., warehouse, work order type, carrier, etc.), performs the translation of the label and sends it to the selected printer (Note that the label can be printed to multiple printers and you can for each printer decide, which layout to use).
    • translate() method is responsible for actually replacing any variables in the label layout with the corresponding values from the WHSLicensePlateLabel table
Let's take a closer look at the fields available on the WHSLicensePlateLabel table:

WHSLicensePlateLabel table fields
These fields represent the full list of potential variables that can be put into the label in the Document Routing layout. Depending on the item flow, some of these fields are not populated. Note that when setting up the placeholders in the Document Routing Layout form, only visible fields are displayed. That allows for storing some "plumbing" information for each record, like the PrinterSettings.

Adding placeholder variables to a label layout

Fragile Example extension

For some scenarios you might want to display some additional fields on the label, which are not part of the list shown above.

Imagine a scenario where we want to display a "Fragile" message on the label if the items on the work line are accordingly marked. 

Since there is no such field in the label, we will need to extend this functionality. And because we are working in AX 7, we will try to use Extension instead of Overlayering the elements, as we would do in AX 2012 R3.

Step 0 - Create a new model for our changes

First of all, let's create a new model, which will contain all the elements we add through extension.

Create a new model in Visual Studio
We will put this model into a new package, as we do not intent to overlayer existing AOT elements. We will reference the Application Suite package, so that we can access the elements defined in it, such as the ones mentioned above. 
Note that by default the new package will also reference Application Platform.

Select referenced packages for model FragileExample
Confirm the creation of the new model and create a new project for it. Name it FragileExample.

Step 1 - Extend the WHSLicensePlateLabel table and add a new field FragileCode

Since we want to display an extra field in the label, we need to add it to the WHSLicensePlateLabel table. In order to do that, let's create an extension of this table in the new model:

Adding a new table extension from Application Explorer
You need to have selected the right project in your Solution Explorer before you attempt creating an extension. This is to ensure that the extension is actually created in the right model.


We are now going to add a new field of base type String, and call it FragileCode with the corresponding label.

Note I am not going to bother with labels or Extended Data types for sake of simplifying the demo

Table extension for WHSLicensePlateLabel in FragileExample model
New field FragileCode on table extension WHSLicensePlateLabel.Extension
In order for the field to appear in the Document routing layouts form all we need to do is compile the new code and synchronize it against the database. I have decided to do that at build time by configuring it accordingly, as shown below:

Synchronize on build = true for FragileExample project
If we now reopen the Document routing layouts form, we can see that our new placeholder is available in the list and we can add it to the label:

Fragile code placeholder available in Document routing layouts form

Step 2 - Populating the Fragile code field on WHSLicensePlateLabel record

Now that we have the field available on the label, we need to populate it. 
As described in the Technical information section above, the label details are populated in the class WHSLicensePlateLabelBuild. Let's quickly examine how this happens:

Sequence of calls to build a license plate label
As you all know, any and all work creation and execution in Dynamics AX happens through the WHSWorkExecuteDisplay* classes, e.g., purchase order registration can execute WHSWorkExecuteDisplayPOItemReceiving, while general work execution flows can execute WHSWorkExecuteDisplayUserDirected, etc.
Within these classes the WHSLicensePlateLabelBuild class is initialized, and the buildLicensePlateLabels() public method is called, when a label needs to be generated and printed.
Depending on the flow, either the insertSingleLabelPrintLine or the insertSingleLabelMenuItem method will be invoked internally, after which the generated label is printed through the printDocument API of the WHSDocumentRouting class, as described above.

Note. insertSingleLabelPrintLine supports the flow where the Print step is part of the work template lines, while insertSingleLabelMenuItem is for the case, where the Print is configured through the mobile device menu item.

So, in order for us to populate the extra new field we need to look into these insert* methods. With the recent AX 2012 R3 changes these methods have been refactored in a way, where most of the logic for populating the WHSLicensePlateLabel table has been pulled out into a new private initLabel() method.

In Dynamics AX 7 this method has been extended (who could have done that? :)) and now has a delegate which is invoked at the end of the method, which means that event handlers can be created for it. With these minor changes, we can now extend this class by subscribing to the event of the initLabel method being invoked, as shown below:

Copy event handler code for labelInitialized delegate
We can now put this generated event handler signature into a new class in our model, as shown below:

Signature of the event handling method for labelInitialized delegate
This is awesome and is one of the new extensibility language features in Dynamics AX 7 - I have subscribed to the event of this method being called without touching the original class at all.
As you can see, instead I have a SubscribesTo attribute that subscribes me to the corresponding event in a static fashion. 

Now all we need to do is actually populate the Fragile code on the label. Let's do that:

Implementation for the event handling method for labelInitialized delegate
As you see above, the code is super simple, all we do is get the Filter code value from the corresponding item, and populate it into the FragileCode field.

Let's now compile everything and give it a go in AX web client.

You can download the VS project file from my OneDrive here.

AX flow steps and result


  1. Modify item A0001, setting Filter Code 4 to a new value called FRAGILE
  2. Create a new purchase order for 10 pcs of A0001 to WH 24
  3. Modify the Mobile device menu item for Purchase receipt to "Print label"
  4. Ensure you have a Document routing record matching WH 24 for Purchase orders
    1. Ensure you have a document routing layout being sent to a label printer
  5. Receive the above purchase order through the WMDP
As a result, you should get a new label created, which will have the Fragile code populated with the value FRAGILE, as below (I've personalized the form to show the new field):

License plate label with the populated Fragile code

Depending on your label layout you would have this message printed on the label as well.

Conclusion


With this blog post I hoped to show you how to extend the existing set of fields available on the license plate labels.

At the same time we took a look at some of the new language constructs, design paradigms and tooling that allows for a much cleaner approach to extending existing functionality, as compared to overlayering, which many are used to.

Consider extending instead of overlayering next time you need to make a change!

Thursday, September 11, 2014

Printing labels with the new Warehouse management solution

Hi all,

This blog post is based on a document I got from Boštjan Golob, a solution architect working with Dynamics AX. You can connect with Boštjan through LinkedIn.

I really enjoyed the information he provided and therefore wanted to share it publicly so all the AX community can learn how to configure and print labels with the new warehouse management solution. So here it is, enjoy:

Step 1: Understanding

How does AX actually print the labels? It is important to know that most label printers currently in use (such as Zebra brand printers) include some sort of a programming language interpreter and that the print job sent to a label printer is actually a small program. This is no different than with larger printers that use Postscript (there are web servers written in Postscript). In label printing, the feature set of a command language is usually quite rich: you can control the cutting of labels, the printer itself knows how to draw barcodes, you can select fonts that it has loaded in its memory and so on. This is an example of a ZPL (Zebra Programming Language) program:

CT~~CD,~CC^~CT~
^XA~TA000~JSN^LT0^MNW^MTD^PON^PMN^LH0,0^JMA^PR2,2~SD15^JUS^LRN^CI0^XZ
^XA
^MMT
^PW320
^LL0160
^LS0
^FT20,58^A0N,28,28^FH\^FDNalepka:^FS
^FT20,81^AAN,18,10^FH\^FD$LicensePlateId$^FS
^BY1,3,17^FT20,106^BCN,,Y,N,N,A
^FD$LicensePlateId$^FS
^PQ1,,,Y^XZ

AX can print a very sophisticated label by sending such a »program« to the label printer, while at the same time knowing absolutely nothing about label layouting. The labels stored in AX are command snippets like above. These are stored in Document routing layouts (Warehouse management – Setup – Document routing – Document routing layouts).
Knowing when to print a label is the job of configuration of menu items and work templates. AX can initiate printing of labels at work creation (for example in receiving scenarios), or during work execution. Which label to print is the job of document routing setup, where we tell the system, based on what we are currently doing, which label to pick and which label to send it to. We well get to configuration later.

Step 2: Getting the label in a text format

In order for this walk-through to be as comprehensive as possible (and as easy to click through without any annoying »this is left as an exercise to the reader« parts), the process of creating a small demo label will be demonstrated. I will use the NiceLabel labeling software (Made in Slovenia).
First, get the NiceLabel Designer Pro from the web site (www.nicelabel.com) and install the 30-day trial. Also download the NiceLabel Label Printer Drivers, as we will need them to create a dummy printer for NiceLabel to adapt the label to. This is important – label designs can be printer dependent and if you use multiple types of printers, it's best to export the labels with NiceLabel set to every different printer you will use the label with!
Before creating the label, follow these steps to create a dummy printer:
  1. Open the »Devices and Printers« in Control Panel, click on any printer and from the toolbar, select Print Server Properties.
  2. In the next form, select the Ports tab.
  3. Click Add Port…
  4. Select Local port and click New Port…
  5. For the port name, enter a file name (I use C:\_TEMP for misc. crap, so I put in C:\_TEMP\ZEBRAPRINT.TXT)
  6. OK out of everything.
  7. Run the installation program for the NiceLabel Printer Drivers and select a printer you would like to use (I have chosen a pretty basic »ZEBRA GC420d ZPL« printer).
  8. Select the port you have just created.
  9. Finish the installation.
  10. Rejoice!
After the printer is ready, open NiceLabel Designer and create a new label, selecting the printer you have just installed. You will get a blank canvas and you can start drawing. I have created the following layout using the Text and Bar code tools:

Label designer

The second text field is a variable field with the variable name LicensePlateId and the same variable is used for the bar code as well. You can design the label with fixed values, but you will then need to change the text in AX. NiceLabel also comes with plenty of out of the box sample labels, including GS1 SSCC label and so on, which can also be used as a starting point.
To get the text version of the label, you can use several options. NiceLabel Designer Pro supports export of templates for use in external systems. First, let's configure the AX specifics for export. Select File – Export – Printer Template Export Configuration and Add a new system. Give it a name (Dynamics AX WHS) and an extension (txt) and in the Variables tab page, enter $[Name]$ in Variables field like so:

NiceLabel Printer Template Export Configuration

This will cause variables used in the label to be exported in the generated file as $VariableName$, which is the format expected by AX when it does its own substitution of variable contents.
After you are done with this definition, click File – Export – Dynamics AX WHS and NiceLabel will create a nice text file which you then copy and paste into AX:

Document routing layouts

Step 3: Configuring document routing

We will use this label as part of the receiving put-away process 24 PO Direct, so we need to configure the document routing record. In the Document routing form, create a record similar to this one:

Document routing

I have used my dummy printer that I have installed on the AOS. Feel free to experiment with other filter values, which you will probably use in production, at least to differentiate to which printer you want to print the label (note to Microsoft: some web browsers can print to locally attached printers that people carry around with them, so printing could also be done via the web interface). Again – to use the printers, AOS will need to be able to print to them. I would strongly suggest using networked printers, not printers connected locally to computers in the warehouse and then shared, at least from experience. It is best to log on locally to the AOS as the AOS service account and check if you can print to the printers.
Lastly, we need to configure the work templates to print the label as part of the process. To do this, insert a Print step in the Work Template. In our example, 24 PO Direct gets another work step:

Work templates
 
The same approach can be used in the Sales order scenario as well, to create shipping labels for the picked license plates.
In case you would like to print your own labels as a part of receiving goods, you can use the "Print label" check-box on Mobile device menu items for several Work creation processes (PO receiving, Report as finished, Load receiving, TO receiving, Kanban). There is also an indirect activity "Reprint label" for later printing of LP labels.

Step 4: Demo

Create a purchase order for some goods and receive it using the mobile device (web or AX form). There will be no visible change from the user perspective, but, if all goes well, the file you set up for the dummy printer should appear and under Inquires – License plate labels, you should be able to see the newly created label. If you open the file using Notepad, it should contain ZPL with LicensePlateId substituted for the actual license plate number:

CT~~CD,~CC^~CT~
^XA~TA000~JSN^LT0^MNW^MTD^PON^PMN^LH0,0^JMA^PR2,2~SD15^JUS^LRN^CI0^XZ
^XA
^MMT
^PW320
^LL0160
^LS0
^FT20,58^A0N,28,28^FH\^FDNalepka:^FS
^FT20,81^AAN,18,10^FH\^FDTESTLP00001^FS
^BY1,3,17^FT20,106^BCN,,Y,N,N,A
^FDTESTLP00001^FS
^PQ1,,,Y^XZ

To see how the label would be printed out, you can use for example the Labelary service. The above example can be rendered using this URL.

Giving a nice result like this:
Printed label

That's it, you are all set to explore this on your own further, and thanks to Boštjan again for providing this comprehensive description of the process.