Code highlighting

Showing posts with label Reports. Show all posts
Showing posts with label Reports. Show all posts

Saturday, October 01, 2016

Tutorial: Label printing in Microsoft Dynamics AX 7

A while back I posted a tutorial on how to configure and use the label printing functionality in Microsoft Dynamics AX 2012 R3:

http://kashperuk.blogspot.dk/2014/09/printing-labels-with-new-warehouse.html

With the release of Microsoft Dynamics AX 7 and the move to the Cloud, printing has become a bit more complicated than before, since the printers are not on the same domain / network as it used to be when everything was installed on-premise.

The major change that I will talk about in this post is the way we set up printers now.

Installing & configuring the Document Routing Agent

The wiki article describing the installation and configuration of the Document routing agent is very well written and contains a lot of details about the restrictions and requirements for this to work, so I will not repeat it here. Here is the link:


For example, one of the unexpected requirements is that Adobe Acrobat Reader is installed.

Note, that with a recent update of the AX platform, the Document Routing Agent can now be run in the background as a Windows service, which adds a number of benefits. Read more about that in the below wiki article:



Now that the document routing agent application / service is installed and can sign in to AX, you can select the printers you want to expose, activate them in the Network printers form, as described in the above wiki article.

After that, they will show up in the Printer name lookup on the Document Routing form as before.

Everything else is pretty much the same from a user standpoint, nothing changed in terms of document routing configuration or the WMDP configuration.

So when a label needs to be printed, here's what happens now:
  1. The label is generated in the ZPL code, as before, containing all the replaced variables from the document routing layout
  2. It is then saved in a file in the Azure Blob Storage (since the AOS does not have access to the client machine, we cannot just go and do something with it) in a pre-configured folder, including all the relevant settings, specifically, which printer to use for this label
  3. The Document Routing Agent application / service on the network printer server or just as a local application one of the network PCs will periodically query if there are any pending files to be printed, downloads them from the Azure Blob storage and, depending on the printer settings defined for the specific file, re-routes the file to be printed, whether that is to a Zebra printer, or, in the case of regular SSRS reports, to a PDF document, or a regular printer.

Note that before Platform Update 2 there was an unpleasant bug in this framework, which prevented printing of labels from the Warehouse Mobile Devices Portal. That has since been fixed, and you should be able to print labels without any problems (Workaround for people on earlier installations is to use the WMDP enumlator form from AX web client)

Give it a try and report back here in case you find some of the instructions unclear, or if something is not working according to your expectations.

Thanks!

Saturday, March 08, 2008

Find all reports with datasources innerjoined 1:n

I haven't really written much X++ code in the past 5 months, so a work-related task on X++ was like a holiday :)

I needed to find all reports, in which there is a child datasource (on some level) that is inner joined with the parent datasource as 1 to many.

Here is one of the possible solutions of the problem.
I would also want to talk a little bit about different methods used in this scenario, in case some of you are still unfamiliar with those:

static void FindReports(Args _args)
{
#AOT
Report report;
TreeNode treeNode = TreeNode::findNode(#ReportsPath);
TreeNodeIterator iterator = treeNode.AOTiterator();
QueryBuildDataSource qbds;

boolean find1nInnerJoin(QueryBuildDataSource _qbdsParent)
{
int i;
QueryBuildDataSource qbdsChild;
boolean ret;
;
for (i = 1; i <= _qbdsParent.childDataSourceCount(); i++)
{
qbdsChild = _qbdsParent.childDataSourceNo(i);
if (qbdsChild)
{
if (qbdsChild.joinMode() == JoinMode::InnerJoin && qbdsChild.fetchMode() == QueryFetchMode::One2Many)
return true;

if (qbdsChild.childDataSourceCount() > 0 && find1nInnerJoin(qbdsChild))
return true;
}
}
return ret;
}
;

treeNode = iterator.next();
while (treeNode)
{
if (treeNode.sysNodeType() == 202) //Report
{
report = treeNode;
if (report && report.query().dataSourceCount() > 1)
{
qbds = report.query().dataSourceNo(1);
if (find1nInnerJoin(qbds))
info(report.name());
}
}
treeNode = iterator.next();
}
}


Here are some keypoints:

1. Notice the use of TreeNodeIterator class. This is an example of an Iterator applied to the AOT. Very convenient and easy to use. You can read more about this class and its methods on MSDN

2. Notice the use of BaseEnums QueryFetchMode and JoinMode - I have seen many developers specifying integer values instead. I would suggest using these enumerations instead - the code will be much easier to read later on.
I don't know the enum for return value of method treeNode.sysNodeType() though. So if someone does, write a comment - it would be nice to know.

3. Notice the use of an implicit conversion from a base type object (TreeNode class) to a derived type object (Report). This is allowed, because, as you know, X++ type system is a "weak" type system. But beware, because this might lead to run-time errors. For example, if you call a method on the Report class, that is not inherited from the TreeNode class, you must be absolutely sure that the object is actually of type Report. Otherwise, you will get a run-time error.
This is why before the conversion, I verify that only Report objects get through to the following code.

4. The last, but not least: Notice the methods exposed by the QueryBuildDataSource class - basically, it allows to receive a lot of information about a specified query. Again, for more information, refer to MSDN