Axaptapedia is now maintained by AgileCadence For more information please click here.

Difference between revisions of "Category:Report development"

From Axaptapedia
Jump to: navigation, search
 
Line 4: Line 4:
  
 
[[Category:Development]]
 
[[Category:Development]]
 +
 +
ItMORPHX T
 +
An introduction to Axapta
 +
X++ and the Morphx
 +
Development Suite
 +
© 2005 Steen Andreasen
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
About this pre-release
 +
This is a pre-release of the chapter Reports from the upcoming Axapta X++ and
 +
Morphx book, MORPHX IT. The book MORPHX IT is expected to be published at
 +
January 2006.
 +
This chapter is covering how to use the report generator in Axapta. Besides this
 +
chapter on reports, the final book will have a report appendix chapter consisting of a
 +
step-by-step guide to the report wizard, description of the properties on a report and a
 +
description of each report system class. All code examples will be available for
 +
download with the final book.
 +
As the chapter is taken out of context you might be missing other parts from the book,
 +
such as the introduction chapter to X++ and Morphx and the report appendix chapter.
 +
The book is written as a practical book, you will be learning by doing. To gain the full
 +
benefit of the content you will have to do the examples in Axapta while reading the
 +
chapter.
 +
Parts of the chapter are still subject to change, however this chapter will give you an
 +
idea of what to expect for the final book.
 +
Warning
 +
You should never try out any of the examples in this chapter in a live environment.
 +
The information in this chapter is provided as is. I or steenandreasen.com cannot be
 +
responsible of any loss or damages arisen from the information containing in this
 +
chapter.
 +
Trademarks
 +
Axapta, Morphx, IntelliMorph, X++ are trademarks of Microsoft Corp.
 +
Other company names and product names can be trademarks belonging to their
 +
respective owners.
 +
Copyright
 +
Copyright 2005, steenandreasen.com. All rights reserved.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
Table of contents
 +
1 REPORTS.............................................................................................4
 +
1.1 Report wizard..................................................................................................4
 +
1.2 Report generator ............................................................................................4
 +
Report query.....................................................................................................7
 +
Templates .........................................................................................................9
 +
Designs...........................................................................................................12
 +
1.3 Methods on a report .....................................................................................19
 +
Report runbase framework .............................................................................22
 +
Dynamic reports .............................................................................................26
 +
Common report methods................................................................................28
 +
Special reports................................................................................................33
 +
1.4 Summary .......................................................................................................40
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
4 of 40
 +
1 Reports
 +
Reports in Axapta are based on a query and a design stored in the AOT. A report
 +
contains no data. The print of a report is a result of the run-time fetch of the data. This
 +
means reports must always be printed having access to the database. When executing
 +
the report you have the option of printing the report immediately, or defining a batch
 +
job for executing the report at a later defined time. Batch processing is normally used
 +
for heavy reports like printing monthly customer balance lists.
 +
Printing reports from Axapta is very flexible, as Morphx provide tools for overriding
 +
the predefined report. By using the Morphx environment you can at runtime filter
 +
data, or even modify the layout of the printed job.
 +
There are two ways of creating reports in Axapta. Either by using the built-in report
 +
wizard, or by using the report generator, located in the AOT under Reports.
 +
Explanation of the single options in the report dialog is out of scope for this chapter.
 +
The chapters focus is on the technical part of reports. You do not need to have
 +
knowledge about the report end user interface to get benefit of this chapter, though it
 +
will be helpful. If you are not familiar with the end user interface for reports you can
 +
get more detailed information by checking the manuals in the standard package.
 +
1.1 Report wizard
 +
The report wizard is a neat tool for non-technical persons to create reports. The wizard
 +
is located in the toolbar menu Tools | Development tools | Wizards | Report wizard.
 +
This would be the place to start out learning about Axapta reports. The report wizard
 +
is an end user tool, which guide you through the steps of creating reports. You have
 +
the option of storing a report created by the wizard in the AOT. This makes the wizard
 +
interesting as it will help you getting familiar with the elements of an Axapta report.
 +
Use the wizard for creating the basic structure of your report. The last modifications
 +
can then be done by using the report generator to edit the stored report.
 +
For a step-by-step guide on how to use the report wizard, see the appendix Reports
 +
appendix.
 +
1.2 Report generator
 +
By checking out what the report wizard generates, you will soon learn the basics
 +
needed for creating your reports from scratch using the report generator. When
 +
starting out creating a new report it can often be an advantage looking at some of the
 +
standard reports in the AOT, before starting creating your own. Find an existing report
 +
matching some of your needs. Simply duplicate an existing report and start doing your
 +
modification to the copy of the report. For locating a report from the menu see the
 +
chapter Intro to X++ and Morphx.
 +
When starting out with the report generator the tutorial reports in the standard package
 +
might be useful. Take a look at the reports prefixed with tutorial_.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
5 of 40
 +
Axapta reports are divided into two parts, the Data Sources node where the data to be
 +
fetched is defined and the Designs node where the presentation of the data is defined.
 +
For an overview of a report see figure 1, report overview.
 +
Note: It is possible to call another report from within a report however, if you miss this feature you
 +
must use X++ instead.
 +
Figure 1, report overview
 +
Example 1: My first report
 +
Elements used from MorphxIt_Report project
 +
� Report, MyReport
 +
� Menu item output, MyReport
 +
Before going into details, start creating a report as seen in figure 1, report overview.
 +
The report is printing customer transactions grouped by customer. The example is
 +
kept simple as the focus is to learn the basic moves building a report. Later in this
 +
chapter, the details will be explained and more features will be added to the example.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
6 of 40
 +
1. Create a new report by right-clicking the Reports node in the AOT and choose
 +
New Report. A new report called "Report1" will be created. Open the property
 +
sheet and rename the report "MyReport".
 +
2. Two levels of data sources are needed for the query, the customer tables as the
 +
first level and the customer transaction table as the second level. Open another
 +
instance of the AOT and drill down the Data Dictionary/Tables node. Pick the
 +
table CustTable. Unfold the Data Sources/Query node in your report, and drag
 +
CustTable to the Data Sources/Query/Data Sources node. Now the first level of
 +
the query has been added. Unfold the CustTable data sources node in your report.
 +
Drag the table CustTrans to the CustTable/Data Sources node.
 +
3. The two data sources of the query must be linked. Otherwise all customer
 +
transaction will be printed for each customer. Go to the CustTrans node in the
 +
Query and set the property Relations to Yes. The node CustTrans/Relations will
 +
now contain an entry linking the two tables.
 +
4. The query part is now ready to fetch data for the report, and the presentation of
 +
data must now be done. Go to the Designs node, right-click and choose New
 +
Report Design. A new design called "ReportDesign1" will be created. Go to the
 +
ReportDesign1 node and enter the text "Customer transactions list" in the Caption
 +
property.
 +
5. Go to the ReportDesign1/AutoDesignSpecs node. Right-click the node and choose
 +
Generate Specs From Query. Your design will now contain two body sections,
 +
one for each table in the query.
 +
6. The last step is to pick the fields to be printed. Right-click the Query node of your
 +
report, and choose Open New Window. This will ease up dragging the fields to be
 +
printed to the design. Pick the fields AccountNum and Name from the CustTable
 +
data source and drag the fields one at the time to the body section node
 +
CustTable_Body. Go to the CustTrans data source and drag the fields Voucher,
 +
TransDate and AmountMST to the body section CustTrans_Body.
 +
7. You now have a report as seen in figure 1, report overview. Try executing the
 +
report by right-clicking on the report name and choose Open. A dialog for
 +
filtering, sorting and other print options will show. For now just click Ok. The
 +
next dialog is the printer dialog. Check the printout is set to screen and click Ok.
 +
Your report will now be printed to the screen.
 +
8. As you might have noticed, the layout has still some finish to be done. To simplify
 +
the example just add a report template to format the heading of the report. Go to
 +
the Designs/reportDesign1 node and locate the property ReportTemplate. Click
 +
the arrow and choose the template InternalList.
 +
9. Run the report again by following step 7. Now your report has been added heading
 +
information like name of the report, page number, date and time. That is it. You
 +
have created your first report!
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
7 of 40
 +
In the MyReport example you did not have to write a single line of code. When
 +
creating reports in Axapta, you will not have to worry about writing code for data
 +
connections and position controls in the layout. Morphx will handle this for you.
 +
Simple create a query and select the column order of your controls in the design. Only
 +
when creating more advanced reports in Axapta like where fetching data is to
 +
complex to fix in a query, or a special design is needed, you will have to use X++.
 +
When you executed the report, two dialogs were shown. Try creating a menu item for
 +
MyReport by dragging MyReport to Menu Items/Output node. You can now execute
 +
your report by right clicking the new menu items and choose Open. The report will
 +
now have only one dialog. This is the dialog the users will see, further details on this
 +
later in the chapter.
 +
For an overview of properties for the single elements in a report see the appendix
 +
Reports appendix.
 +
Report query
 +
Fetching data for a report is usually done using a query. In the query the data sources
 +
used for the report and the relation between the data sources are defined. It is optional
 +
using a query, as there can be situations where a query cannot fulfill the needs, and
 +
you will have to fetch your data by using X++. The report generator is using a
 +
standard Axapta query. For more information on building a query, see the chapter
 +
Queries.
 +
Before building your query, you will have to decide which tables to be used. The
 +
typical situation will be that you have data from a single form or related forms that
 +
you want to print, as seen in the MyReport example. For help on how to locate tables
 +
and fields from the forms, see the chapter Intro to X++ and Morphx.
 +
When you have localized the tables to be used you must, before adding the tables as
 +
data source to the report query, decide how the sorting and the filtering of the report
 +
must be. This is a performance issue as you will have a must faster report if you have
 +
done some thoughts of this before carrying on. If you are to traverse a lot of inventory
 +
transactions filtering of data at the third level of a query might not be a good idea. A
 +
rule of thumb can be, that if your report is filtering data at the third level of the query,
 +
your design might be wrong. What you are trying to accomplish might have to be two
 +
reports rather than one, or you should consider using a temporary table to build the
 +
result to be printed.
 +
The common situation will be to add all tables needed for the report to the Query
 +
node. If data from related tables are used, the tables must be joined and added at each
 +
level in the query as done with CustTable and CustTrans in the MyReport example. If
 +
you are fetching data from more tables which there are no direct relations in between,
 +
but still the printout of the second level table is dependent on the first level table, you
 +
will either have to add all tables to the query, or just add the first level table as a data
 +
source and use X++ for fetching the second level. Say you want to print sales invoice
 +
lines group by customer. There is no direct relation between the customer table and
 +
the sales invoice lines table, so the table sales invoice journal must be used to build
 +
the query, see figure 2, relation between CustTable and CustInvoiceTrans. The
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
8 of 40
 +
recommended solution will be adding all 3 tables as data sources, as this give the user
 +
the full benefit of the report dialog.
 +
CustTable
 +
Customers
 +
77 dat
 +
InvoiceAccount CustInvoiceJour
 +
Customer invoice journal
 +
62 dat
 +
+CustInvoiceJour CustInvoiceTr ans
 +
Customer invoice lines
 +
64 dat
 +
Figure 2, relation between CustTable and CustInvoiceTrans
 +
Joining data sources can be done in two ways. If the data sources already have a
 +
relation, the property Relation on the lower level data source must be set to true, just
 +
as in the MyReport example. The relation will then be visible under the Relations
 +
node for the joined data source. If no relation shows up you must manually create the
 +
relation under the Relations node, the property Relation must then be set to false. It is
 +
recommend using an already existing relation, rather than manually creating your
 +
own, as changes to the data dictionary would then automatically reflect the report. The
 +
data sources will by default be joined using inner join, but the join mode can be
 +
changed on the properties for the joined data source. Inner joins are the most
 +
frequently used for reports, as the common case is that you have data in a main table
 +
and want to print the related transactions. However if you want to print all records
 +
from the main table even if there are no transactions, you will have to change the join
 +
mode to OuterJoin on the transaction table.
 +
By right-clicking the Fields node you can choose to add a field or an aggregated
 +
function. By default all the fields from the current table are listed and so it will make
 +
no sense adding additional fields from the table. If you add an aggregate function, all
 +
the fields will be removed, as you cannot use both. To remove the aggregate functions
 +
and restore the field list, change the Fields node property Dynamic to Yes.
 +
The aggregate functions can be used if you want to count the number of customers
 +
group by customer group. You will have to select a table, choose an aggregate
 +
function and fields to be used.
 +
Example 2: Aggregate function
 +
Elements used from MorphxIt_Report project
 +
� Report, MyReport_aggregate
 +
� Menu item output, MyReport_aggregate
 +
In the following example a count of customer by customer group will be made. The
 +
design is simplified to focus on the aggregate functions.
 +
1. Add the customer table to the report query, then go to the Fields node right-click
 +
and choose the aggregate function Count. The count field must be AccountNum.
 +
2. Go to the CustTable data source and set the property OrderMode to Group by.
 +
Selecting data is always default done using order by. The last step is adding the
 +
fields to be sort by. Go to the Sorting node and add the field CustGroup. You will
 +
now have a query as seen in figure 3, aggregate function.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
9 of 40
 +
Figure 3, aggregate function
 +
3. Now create a design to print the result. Create an auto design and choose Generate
 +
Specs From Query as done in the MyReport example. You will now have a body
 +
section for CustTable with one control printing the field CustGroup. Add the field
 +
AccountNum.
 +
4. Run the report. A row will be printed for each customer group. The AccountNum
 +
field will count the number of customers in each customer group.
 +
When using aggregate functions data must be selected using group by. The compiler
 +
will give an error if trying to select order by. This makes sense as data are selected
 +
record by record when using order by. Using group by records will be group by the
 +
sorting fields. This means only fields added as sorting fields will contain a value when
 +
using group by. You can add as many aggregate functions as needed. The case could
 +
be that you want to print a transaction list with an aggregation for min, max and
 +
average amounts.
 +
The Sorting node under the Data Sources node is used for setting the sorting of the
 +
report. This can be done either by using indexes or choosing fields. At least one index
 +
or sorting field should be defined. Fields chosen for sorting can at runtime be changed
 +
by the user. Keep in mind that the use of a field for sorting rather than an index may
 +
slow down your report.
 +
The sorting fields added, have a property called AutoSum, this is used if you want
 +
Morphx to print sub totals when the value of the field changes. In the section Auto
 +
design further details on using auto sums will be explained.
 +
The default ranges are specified using the Range node under the data source node.
 +
The user can at runtime add additional ranges or remove the default ranges depending
 +
on the property settings for the range. You can specify a default value for a range, and
 +
if the user may not change the range, the range can be locked or hidden from the
 +
properties. If no ranges have been specified, the first element of each index for the
 +
table will be used as default ranges at runtime. Try executing the report MyReport.
 +
You will see a default set of ranges has been added. Now go back and add the fields
 +
AccountNum to Data sources/CustTable/Ranges node. When executing MyReport
 +
only the range AccountNum will be listed.
 +
Templates
 +
In Axapta you have two different types of templates, report templates and section
 +
templates. The templates are located as the two first entries under the Report node in
 +
the AOT.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
10 of 40
 +
Report template
 +
For the basic formatting of you reports like printing header and footer information, a
 +
report templates will be the right choice. You can use templates for more advanced
 +
cases like using data from specific tables, but it will make your templates less generic.
 +
Report templates are usually used for information not related to a specific table, like
 +
caption, page numbering and lines. The template InternalList used in the MyReport
 +
example is a commonly used report template, which is formatting the caption name,
 +
setting company name, page number, initials, date and time. You can create report
 +
templates with controls using a specific table. Just keep in mind that your tables must
 +
be declared and fetched in your report. To view the template, localize the template in
 +
the AOT, open the visual editor by right-clicking the template node and choose Edit.
 +
Example 3: Report template
 +
Elements used from MorphxIt_Report project
 +
� Report template, MyInternalList
 +
� Report, MyReport_MyInternalList
 +
Create a new template based on the InternalList template. InternalList contains basic
 +
header formatting. A prolog and an epilog section will be added to the new template.
 +
The new report template will be used to extend the MyReport example.
 +
1. Start duplicating MyReport and rename the new report to
 +
MyReport_MyInternalList.
 +
2. Go and locate the report template InternalList in the AOT. Right-click the report
 +
template and choose Duplicate. Rename the new report template to
 +
'MyInternalList'.
 +
3. Right-click the report template name, choose New and select the report section
 +
Prolog. The prolog will contain a text and a new page feed. First the text to be
 +
printed must be defined. Go to Prolog/Methods, right-click and choose New
 +
Method. Open the new method and enter the following:
 +
display description prologDescription()
 +
{
 +
return strFmt("Start of report: %1", element.design().lookupCaption());
 +
}
 +
The method must now be used in the design for the prolog. Close the editor and
 +
drag the method to the Prolog node. A string control which will return the value
 +
of the display method prologDescription() has now been created.
 +
4. Right-click the report template name, choose New and select the report section
 +
Epilog. Now created a method as in following, and drag the method to the Epilog
 +
node.
 +
display description epilogDescription()
 +
{
 +
return strFmt("End of report: %1", element.design().lookupCaption());
 +
}
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
11 of 40
 +
5. To have the prolog and epilog sections printed on new pages a new page feed
 +
must be added. Go to Prolog/Methods, right-click and choose Override Method
 +
and select executeSection(). Add the following to the method:
 +
public void executeSection()
 +
{
 +
super();
 +
element.newPage();
 +
}
 +
6. Add a new page feed to the epilog section. The new page feed must be executed
 +
before super() in the epilog section, as the epilog must be printed on a new page.
 +
You will now have a report template as seen in figure 4, report template.
 +
Figure 4, report template
 +
7. Next step is to use the new report template in MyReport_MyInternalList. Go to
 +
the node Designs/ReportDesign1, open the property sheet and select
 +
MyInternalTemplate as the report template.
 +
8. Create a new menu item for the report. When the report is executed a page for the
 +
prolog will be printed before the report, and a page for the epilog will be printed
 +
after the report.
 +
The MyInternalList template uses display methods for returning the values for the
 +
controls. As for forms, display methods are often used for reports. This is a common
 +
way of printing data which is not part of a query. It could as here be text, or it could
 +
be a calculation, which is not suited to be a part of the query. Simple create your
 +
display method and drag the method to the design. You do not have to worry about
 +
the type of the control to display the value appropriate. Morphx will handle this for
 +
you by checking the methods return type.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
12 of 40
 +
You ought to consider having one or two report templates, to be used for your reports.
 +
The advantage of using a report template for the basic formatting is to have identical
 +
presentation of your reports in an easy way. If you later on decide to change a report
 +
template, all reports with auto designs using the report template will automatically be
 +
changed.
 +
Section template
 +
The section templates were introduced in version 3.0. This could be the reason why
 +
they are rarely used and that you might not find any examples of their use in the
 +
standard package. A section template is using a table map as offset. Table maps are
 +
explained in the chapter Data Dictionary. Fields from the map can then be added as
 +
controls. In cases where you have reports with similar section blocks, the use of a
 +
section template could be the answer, rather than building the same block section in
 +
two different reports. In practice you might want to create one report and handle the
 +
control of your report using X++, rather than having two reports and using a section
 +
template. The SalesInvoice report is and example of this.
 +
Designs
 +
Positioning fields and controls in your design are normally handled by IntelliMorph.
 +
All controls will by default be auto adjusted as much as possible. This means that the
 +
controls will be set to auto positioning and fonts and font size is defaulted from the
 +
user options in the toolbar menu Tools | Options in the tab page Fonts.
 +
When you have chosen the row order Axapta will be positioning the controls based on
 +
the information from the extended data types. This is very helpful, as if you want to
 +
add a control in the middle of a row, or you want to hide a control, the following
 +
controls will be positioned accordingly. It is recommended to auto position controls as
 +
you get the full benefit of IntelliMorph, however in situations where your controls
 +
must always have a fixed position as formulas you can override the default settings.
 +
The disadvantage is that if you set a single control in a row to a fix position, you will
 +
have to define fixed positions for all controls. This cannot be recommended unless
 +
your controls must fit the layout of say a formula.
 +
If you are to print controls which must be positioned below each other in the same
 +
column you should consider using the property ModelFieldName which all controls
 +
on a report have. The current control will adjust to the name of the control specified in
 +
ModelFieldName, if the current controls positioning is set to auto.
 +
Creating design
 +
A report can have more than one design. Under the Design node, you can create as
 +
many designs as needed. This can be useful if you have a formula you want to print
 +
with different layout for each language or group of customers. More designs in one
 +
report are not that often used anymore in the standard package. Instead of having
 +
several designs the need for different layouts is handled by X++, see the report
 +
SalesInvoice. In the SalesInvoice report, the method element.changeDesign() is
 +
handling whether a control is to be printed or not. The benefit of always using a single
 +
design is that it is more time consuming maintaining differences in the design rather
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
13 of 40
 +
than doing it from X++, like the SalesInvoice report, maintaining header sections in
 +
several designs is hard work as it will take time to locate and verify that your changes
 +
are identical in all designs.
 +
Note: If you are creating a report like a formula which must fit a preprinted layout it can be necessary
 +
to do the final adjustment using the specific printer driver, as the layout will be adjusted according to
 +
the single printer driver.
 +
Designs can be created either as auto design or as a generated design. A design can
 +
also consist of both an auto design and a generated design. In this case only the
 +
generated design will be used. The main differences between auto designs and
 +
generated designs are that auto designs give the opportunity for using dynamic
 +
templates, auto headers and auto sums. Generated designs are static. In other words,
 +
you will get the full benefit of using auto designs, as you are using the power of
 +
IntelliMorph. It is recommended using auto designs, only in special case where a fix
 +
layout is needed, like layout of a cheque or external formulas, generated designs can
 +
be an advantage.
 +
Generated designs have some extra sections for adding headers and footers to body
 +
sections. Beside that auto designs and generated designs are using the same type of
 +
sections. See figure 5, report design sections for an overview of sections in a report
 +
design.
 +
Type Description
 +
Prolog
 +
This is the first section printed. The prolog is
 +
typically used for printing a logo or a title on the
 +
first page.
 +
Page Header The page header is printed at the top of each page.
 +
A report can have more than one page header.
 +
Body The body section is printed after the page header.
 +
This is the data section. The report will normally
 +
contain a body section for each data source.
 +
Page Footer Page footer is printed at the bottom of each page.
 +
A report can have more than one page footer.
 +
Epilog This is the last page printed.
 +
Programmable Section Programmable sections are executed from code.
 +
This type of sections can be used in cases, where
 +
you need to print data which is not part of the
 +
query.
 +
Section Template Section Templates is used for defining common
 +
used data, typically used in body sections. A
 +
section is based on a Map.
 +
Header Header is used in Generated Designs as body
 +
header.
 +
Section Group In Generated Designs, the Body section is added
 +
to a Section Group.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
14 of 40
 +
Footer Footer is used in Generated Designs as footer for
 +
a body section.
 +
Figure 5, report design sections
 +
You have two options for adding controls to your design, either by using the nodes in
 +
the report tree as shown in previous examples, or by using the visual editor. The
 +
visual editor gives the option of either viewing or editing the controls in your design.
 +
To edit double-click the design node, and to view right-click the design node and
 +
choose View.
 +
The visual editor has a view close to the printed result. If a report has a complex
 +
design like the SalesInvoice report, it can be difficult to figure out how the result will
 +
look on print. For more simple report, the editor is at better choice. You have the same
 +
features in the visual editor as if you are using the AOT for creating your design.
 +
From the visual editor you can change the properties for an element of the report and
 +
add or delete controls. To modify the report from the editor, simple position the cursor
 +
and right-click for the menu to edit, delete or add an element. To change the unit for
 +
the ruler right-click and choose between centimeters, inches or chars.
 +
Figure 6, The visual editor
 +
This sounds all very well, but in practice the visual editor is best for just getting an
 +
overview of your design or to locate and change the properties for the control. The
 +
visual editor is acting slowly. Try opening the property sheet on top of the visual
 +
editor and drag the property sheet around. All contest of the editor is redrawn. This is
 +
just not good enough. Do not waste too much time on this.
 +
Auto design
 +
The most common way of creating the layout for your reports is by using auto
 +
designs. When using auto designs, you will only have to choose a report template and
 +
the fields to be printed from the query. IntelliMorph will handle formatting the layout.
 +
If your report contains integer or real fields, the user will at runtime have the option of
 +
choosing summarization.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
15 of 40
 +
For a quick start creating your auto design, it can be an advantage right-clicking the
 +
auto design node and choose Generate Specs From Query, a body section will then be
 +
created for each data source in the query, and the sorting fields will be added as
 +
controls to the design.
 +
To get a visual view of your report, right click the auto design node and select View.
 +
Try opening MyReport in the visual editor. Notice that the visual editor will show the
 +
sections from the report template, even though the template sections are not a part of
 +
the reports nodes. This is pretty neat, and it helps give an overview of the report. To
 +
edit the report right click and choose edit. In edit mode only the nodes which are part
 +
of the report are accessible.
 +
The use of auto sums is a useful feature in auto designs, as the end user can define the
 +
levels to be summed at runtime. From the report dialog you can set break levels for
 +
sums for any field or body section and finally you can set a total for the whole report.
 +
From an end users point of view it might be the most important reason for using auto
 +
designs. It makes your report more flexible, and at last it might save you time not
 +
having to hardcode any sums.
 +
Example 4: Auto sum
 +
Elements used from MorphxIt_Report project
 +
� Report, MyReport_Sums
 +
� Menu item output, MyReport_Sums
 +
Extend MyReport with totals for the transaction amounts. A sub total for each
 +
customer and a total for all customers will be added.
 +
1. Start duplicating MyReport and rename the new report to MyReport_Sums
 +
2. As an index does not have an option setting break points for totals, the index
 +
AccountIdx will be removed from the CustTable data source, and the fields
 +
AccountNum will be added instead. Set the property AutoSum to Yes for the field
 +
AccountNum. You have now defined that each time the value of a customer
 +
account changes a sub total will be printed.
 +
3. The fields to sum up must be defined. In this example only the fields AmountMST
 +
from the table CustTrans will be used. Find the control printing AmountMST in
 +
the CustTrans body section, open the property sheet and set SumAll to Yes.
 +
4. Create a new menu item for the report, and run the report. For each customer a sub
 +
total will be printed.
 +
5. As you notice only a sub total was printed. To add a total for the whole report,
 +
close the report and go to the AOT again. Go to body section for CustTable and
 +
set the property GrandTotal to Yes. The body section CustTrans does not have
 +
this property, only CustTable have the property as it is the primary data source.
 +
In step 2 the control to be summed was defined. In the design it was specified that
 +
AmountMST must be summed. This is in fact the only settings needed to be done.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
16 of 40
 +
The user will at runtime be able to do the rest. The sorting field for breaking the sub
 +
total and the settings for the grand total will just be the default settings for the report.
 +
The AutoDesignSpecs node has a property called GrandTotal. This property will print
 +
a total for the report with the default label 'Super Grand Total' if set to Yes. Super
 +
Grand Total and the grand total set from either the report dialog or the body section
 +
will always give the same result. Both will print a total for the whole report. So if the
 +
user has the option of setting the grand total from the report dialog, you should not use
 +
the super grand total.
 +
As you might have noticed there is a property called auto header. This is used in the
 +
same way as auto sum. Instead of printing totals, a header will be printed each time a
 +
sorting field breaks. The user can control auto header at runtime, but if needed you
 +
can default auto headers to be visible. Both auto sums and auto headers are features
 +
only available in auto designs.
 +
All sections mentioned so far are triggered by either the reports framework or the
 +
reports query. You will have situations where you need to trigger a report section
 +
manually. Programmable sections are the answer for this, and as you might have
 +
guessed programmable sections are executed from X++.
 +
Example 5: Programmable section
 +
Elements used from MorphxIt_Report project
 +
� Report, MyReport_ProgSec
 +
� Menu item output, MyReport_ProgSec
 +
A programmable section is added to MyReport. For the simplicity the section will just
 +
print a text control.
 +
1. Start duplicating MyReport, and rename the new report MyReport_ProgSec. Go to
 +
the report design node AutoDesignSpecs, right-click the node and choose new
 +
ProgrammableSection.
 +
2. Open the property sheet for the new programmable section and locate the property
 +
ControlNumber. The control number is used to reference the section from X++.
 +
Set the ControlNumber to 10.
 +
3. Now add a control to the programmable section. Right click the programmable
 +
section and choose New Control to add a text control. Go to the new text control,
 +
located the property Text and enter 'Header for customers'.
 +
4. Now define the execution of the programmable section. Go to
 +
MyReport_ProgSec/Methods, right-click and choose Override Method and select
 +
init(). The init() method must look like:
 +
Public void init()
 +
{
 +
super();
 +
element.execute(10);
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
17 of 40
 +
}
 +
5. Create a menu item for the report MyReport_ProgSec. When executing
 +
MyReport_ProgSec the text 'Header for customers' will be printed before the
 +
query is traversed.
 +
In the example the number for the programmable section was changed to 10. It is
 +
recommended to have steps between your programmable sections. If you at a later
 +
time need to add a new programmable section you will still be able to give your
 +
programmable sections a logical numbering.
 +
The execution of a programmable section can be called from X++ where needed.
 +
However when used in combination with auto sums some reservations must be taken.
 +
Say you want to print a programmable section before a body section is printed, then
 +
the logical place to add your code will be in the executeSection() method just before
 +
super() in the body section. This will print your programmable section before the
 +
body section is printed alright, but the programmable section will also be executed
 +
before each auto sum. At runtime Morphx treats an auto sums as a footer, and this
 +
causes the body section to be executed again. The solution is to use the report
 +
methods header() or footer() instead. From here you can control which body section is
 +
being executed. When a body section is executed, the parameters _tableId and
 +
_fieldId will contain a value.
 +
public void header(ReportSection _headerSection, tableId _tableId, fieldId _fieldId)
 +
{
 +
if (_tableId == tableNum(custTable) && _fieldId)
 +
element.execute(10);
 +
super(_headerSection, _tableId, _fieldId);
 +
}
 +
Here the header() method is used. A check is made to secure that it is the body section
 +
for the customer table which is printed. If so the programmable section is executed.
 +
Another way of using the header() and footer() method could be to add a new page
 +
after a sum if you are printing a batch of vendor or customer transactions.
 +
Note: Programmable sections are often used to insert separators like row spaces or lines. Doing so can
 +
be done by setting the properties of the programmable section. You will need to add a "dummy" control
 +
to your programmable section as if the programmable section does not have any controls the section
 +
will not be printed.
 +
Generated design
 +
Using generated designs can at first glance seem easier to use than auto designs, as
 +
you have more sections to construct your report from and all sections are visible in the
 +
report. However by using the generated designs your report will be more static as you
 +
generate the design based on the settings from the query and property settings from
 +
the design node. The disadvantages are that if you have chosen a report template for
 +
your report and later on decide to change the report template or simply choose another
 +
template, it will not reflect your design. Secondly the user will not be able to choose
 +
summarization when executing the report.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
18 of 40
 +
To have a closer look at a generated design MyReport will be used to create a
 +
generated design. Go to MyReport and localize the node Designs/AutoDesign1. Right
 +
click the node and choose Generate Design. A new node called Generated Design has
 +
now been created below Designs/AutoDesign1. If you unfold the generated design
 +
you will notice that a design similar to the auto design is created. The difference is
 +
that the sections from the report template and the sections form calculating totals have
 +
been added.
 +
If you are missing the overview of you report when using auto designs, the option to
 +
create a generated design based on your auto design can come in handy, as all sections
 +
based on your templates and auto sums will be populated.
 +
Controls in design
 +
The most common way to add controls to your design is by dragging fields or display
 +
methods from a data source or direct from a table. When dragging a field or display
 +
method Morphx will create a control of the same type. This goes for basic types like
 +
string, enum, integer, real date and time. The controls like prompt, shape, sum and
 +
field group are used for more special purposes and must be added manually. You can
 +
of course add all type of controls manually, but it speeds up just dragging the controls
 +
as Morphx will auto position the control and fill out the properties with reference to
 +
either the field or the display method.
 +
Auto designs and generated designs have the same controls. For an overview of the
 +
available controls see figure 7, report controls.
 +
Name Description
 +
String
 +
Used for string values. If printing memo fields, the property
 +
DynamicHeight auto adjust the height of the control
 +
according to the number of lines printed.
 +
Enum
 +
Used to print the value of base enums.
 +
Integer
 +
Used for integer values.
 +
Real Used for real values.
 +
Date Used for printing dates. Dates will be formatted accordingly
 +
to the Windows regional settings.
 +
Time Used for printing the time. Time will be formatted
 +
accordingly to the Windows regional settings.
 +
Text Used for printing fixed text values. If the text must contain a
 +
dynamic value, display methods returning a string is
 +
normally used.
 +
Prompt Prompt will add text with following dots and a colon to the
 +
text.
 +
Shape Will draw a box. Size and position is specified by the
 +
properties. Can be used to build the layout for a formula.
 +
Bitmap Used for printing graphic. Enter path to the bitmap, refer to a
 +
container or use resources. For an example of using a
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
19 of 40
 +
resource as a bitmap, see the report HRMApplicantStatus.
 +
Sum Used for printing sums with generated designs. With auto
 +
designs sums can be used for printing a sum in a
 +
programmable section. For an example of the use in auto
 +
design, see the report SalesLinesExtended.
 +
Field group Used for adding a field group from the data dictionary. The
 +
field group on the report will automatically be updated if
 +
changes are made to the field group in the data dictionary.
 +
Figure 7, report controls
 +
Bitmap can be at bit tricky to use as different options exist. You can use icons from
 +
Axapta resources. Either you key-in the resource id in the property sheet or you can
 +
make a display method returning the resource id. To get an overview of the available
 +
resource use the report tutorial_Resources, the report prints the resource id and the
 +
corresponding icon. Other options are to enter a path to a bitmap or refer to a bitmap
 +
stored in a container. When referring to a path remember using double backslash.
 +
The sum control is in generated designs used in the footer section. When used in auto
 +
designs the sum control must be put in a programmable section.
 +
Note: When printing a report the infolog may say that the report is scaled to fit the page. This is caused
 +
by too many columns in your design. Set the property FitToPage to No on the design node to disable
 +
scaling.
 +
When adding controls to your design you must assure that controls referring to a data
 +
source are fetched at the time the section is printed. That means, if looking at the
 +
MyReport example, you cannot print a control from the body sections CustTable and
 +
CustTrans in a page header section, because when the page header is sent the body
 +
sections are not fetched yet. The same goes for adding a control to the body section
 +
CustTable with reference to a field from CustTrans. This will also give an error as
 +
CustTable is fetched before CustTrans.
 +
1.3 Methods on a report
 +
Creating simple reports like printing a list of inventory items, your needs can
 +
normally be fulfilled without having to write X++ code and simple using the facilities
 +
the report generator provide. For more advanced needs like filtering data based on a
 +
caller or a specific sorting you will need to override the methods on the report. For an
 +
overview of the methods on a report see figure 8, report methods. The query
 +
methods are described in the query chapter, see Queries.
 +
The report system classes are often used when modifying reports at runtime. They are
 +
the fundamental parts of reports and they give the opportunity to have a handle on
 +
each element of a report. In fact, you can create a report from scratch using the system
 +
classes. For more information on the system classes see the chapter Classes. For a
 +
complete reference on report system methods, see the appendix Reports appendix.
 +
Name Parameters Description
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
20 of 40
 +
CallMenuFunction
 +
MenuFunction _menuFunction WebReport property
 +
Caption
 +
str _reportSpelling,
 +
str _reportName,
 +
str _designCaption,
 +
str _designName
 +
Used for setting the caption for the
 +
report. The parameters _reportSpelling
 +
and _designCaption set the captions in
 +
the print to screen window.
 +
CreateProgressForm
 +
Overloads the standard progress form
 +
executed when the report pages are
 +
created. The method gives the option
 +
to create your own progress form.
 +
Dialog
 +
Object _dialog Dialog() is used when adding fields to
 +
the reports dialog. The report runbase
 +
framework will call the dialog when
 +
the report is executed. See report
 +
KMAction.
 +
Fetch
 +
This method is the engine of the
 +
report. Fetch() is opening the user
 +
dialog, selecting the records from the
 +
database by processing the query and
 +
sending the records to be printed.
 +
This method is overloaded, when an
 +
expression cannot be specified in a
 +
query. An example could be printing
 +
detail information, see report
 +
HRMCourseSkills.
 +
Footer
 +
ReportSection _footerSection,
 +
tableId _tableId,
 +
fielded _fieldId
 +
The method is triggered each time a
 +
section in the design is executed. As
 +
auto sums is not a part of the design,
 +
this gives the option to execute code
 +
before or after auto sums is printed.
 +
GetTarget
 +
Returns the selected print medium.
 +
Header
 +
ReportSection _headerSection,
 +
tableId _tableId,
 +
fieldId _fieldId
 +
The method is triggered each time a
 +
section in the design is executed. As
 +
auto sums is not a part of the design,
 +
this gives the option to execute code
 +
before or after auto sums is printed.
 +
Init
 +
This is the first method called. The
 +
method is initializing the report.
 +
Entities used in the report are typically
 +
initialized here. See report
 +
salesFreightSlip.
 +
New
 +
anytype _argsOrReportOrContainer,
 +
str _designName,
 +
boolean _isWebReport=FALSE
 +
Used to initialize a reportRun object.
 +
This is normally not done from the
 +
report generator. The common case
 +
would be if a report is initialized from
 +
X++.
 +
Pack
 +
This method is used for storing last
 +
values. It is used in conjunction with
 +
unpack(), which is loading the last
 +
value stored. However unpack() is not
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
21 of 40
 +
a base method. If a new dialog field
 +
has been added, pack() is overloaded
 +
to store the values from the dialog. See
 +
report KMAction.
 +
PageFormatting
 +
The method is not used anymore.
 +
From version 3.0 the method
 +
PrintJobSetttings.PageFormatting() is
 +
used instead.
 +
Print
 +
Print() is returning the number of
 +
pages to be printed. The method can
 +
be used to check whether there are any
 +
pages to print. See report
 +
projTimeSheetEmpl.
 +
PrinterSettings
 +
int _showWhat=-1 Used to activate the different parts of
 +
the printer dialog. The method is only
 +
called if the report runbase framework
 +
is not in use, as it is called from the
 +
prompt() method.
 +
ProgressInfo
 +
int _pageNo,
 +
int _lineNo
 +
ProgressInfo is executed for each line
 +
printed on the form.
 +
Prompt
 +
boolean _enableCopy=TRUE,
 +
boolean _enablePages=TRUE,
 +
boolean _enableDevice=TRUE,
 +
boolean _enableProperties=TRUE,
 +
boolean _enablePrintTo=TRUE
 +
Prior to version 3.0 prompt() was
 +
handling report dialog. Now the
 +
dialog() method is used instead. The
 +
method cannot be used in combination
 +
with the report runbase framework as
 +
the framework will overrule the
 +
settings. The class PrintJobSettings
 +
must be used instead.
 +
Run
 +
Run() is called when the Ok button is
 +
pressed in the dialog. Run() does the
 +
following steps.
 +
• If no generated design exists,
 +
a design is created on the fly
 +
based on the auto design.
 +
• Calling fetch()
 +
• Calling print()
 +
The method can be used for adding
 +
ranges to the query after the based on
 +
settings in the dialog. See report
 +
ReqPO.
 +
Send
 +
Send() is related to fetch(). Fetch() is
 +
iterating through the query records,
 +
and send() is sending the records to the
 +
design.
 +
The method can be overloaded to
 +
validate whether the record should be
 +
printed. See report CustTransList.
 +
SetTarget
 +
PrintMedium _target Set the target media for the report
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
22 of 40
 +
ShowMenuFunction
 +
MenuFunction _menuFunction WebReport property
 +
ShowMenuReference
 +
WebMenu _menuReference WebReport property
 +
Title
 +
str _title="" Not of much use anymore. Can
 +
override the caption bar when printed
 +
to screen, if executed from fetch().
 +
ExecuteSection
 +
Each section in the design has the
 +
executeSection method, which is used
 +
to print the section. The method can be
 +
used to validate whether the section
 +
must be printed. See report
 +
CustCollectionJour.
 +
Figure 8, report methods
 +
The previous parts of this chapter have focused on the single elements reports consist
 +
of. Now it is time to dig into how to use X++ for modifying your reports.
 +
Report runbase framework
 +
You might have wondered why you sometimes get different dialogs when executing
 +
your report. If a report is executed from the AOT you will first get the query dialog
 +
and second the printer dialog. When a report is executed from a menu item you will
 +
get only one dialog. By version 3.0 of Axapta a new runbase class runbaseReportStd
 +
was introduced. If a report is called from a menu item RunbaseReportStd will be
 +
called from the class SysReportRun. This means that the class runbaseReportStd will
 +
be called by the report runbase framework if your report is not called from a class
 +
inherited from the runBaseReport class. However when a report is executed directly
 +
from the Reports node the runbase report framework is not executed, and the two
 +
dialogs are shown.
 +
Note: reports are often used to follow up on data in the system. Here it might be neat having the report
 +
updating data. In Axapta it is best practice not to have reports writing to the database. If your report
 +
must update or insert records, you should consider creating a class doing the data manipulation and
 +
have the class called from a class inherited from the report runbase framework.
 +
It is good practice always to create a menu item to execute your report as you will
 +
then have the same dialog shown as the users. Keep in mind that a report executed
 +
from a class always must be inherited from the class runBaseReport. The class
 +
RunbaseReportStd is only to be used by the framework. For more information on the
 +
runbase classes, see the chapter Classes.
 +
Figure 9, Runbase report classes
 +
In prior versions of Axapta it was a common rule when creating a report that the
 +
report had to be called from an inherited runBaseReport class. This was done to wrap
 +
the two above mentioned report dialogs, making the report batch able and for better
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
23 of 40
 +
performance as the class can be set to run on the server. With the introduction of the
 +
runBaseReportStd class only reports with heavy database load should be inherited
 +
from the runBaseReport class.
 +
Example 6: Report runbase
 +
Elements used from MorphxIt_Report project
 +
� Class, SalesReport_DailyEntries
 +
� Report, SalesDailyEntries
 +
� Menu item output, SalesReport_DailyEntries
 +
To get hands on the report runbase framework go to the Classes node and locate the
 +
class SalesReport_DailyEntries. The class is inherited from the runBaseReport class.
 +
This is a common report class named with a prefix for the module. You will see a lot
 +
of similar report classes when traversing the application classes.
 +
SalesReport_DailyEntries calls the report DailyEntries. Actually this class is of no
 +
need in v3.0 of Axapta, as the logic of the class is handled by the runBaseReportStd
 +
class. The class SalesReport_DailyEntries is a good example of how to construct a
 +
report class so it is used to simplify the case. The class has the following methods:
 +
public identifiername lastValueElementName()
 +
{
 +
return reportStr(SalesDailyEntries);
 +
}
 +
Here the name of the report is specified. The method must be overloaded, as the
 +
method specifies the name of the report. The function reportStr() secures that the
 +
report name entered is a valid report name.
 +
client server public static ClassDescription description()
 +
{
 +
return "@SYS77491";
 +
}
 +
This method is defining the caption name for the report dialog. This method is
 +
optional.
 +
static void main(Args args)
 +
{
 +
SalesReport_DailyEntries salesReport_DailyEntries;
 +
;
 +
salesReport_DailyEntries = new salesReport_DailyEntries();
 +
if (salesReport_DailyEntries.prompt())
 +
{
 +
salesReport_DailyEntries.run();
 +
}
 +
}
 +
The main() method is a static method which initializes the class. This makes the class
 +
run able so the class can be executed from a menu item. A check is made to validate
 +
whether the dialog is called. If Ok is pressed in the dialog, the report is executed.
 +
For using the class in the report the following have been added:
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
24 of 40
 +
public class ReportRun extends ObjectRun
 +
{
 +
SalesReport_DailyEntries salesReport_DailyEntries;
 +
}
 +
In classDeclaration a variable for the class salesReport_DailyEntries is declared.
 +
public void init()
 +
{
 +
super();
 +
salesReport_DailyEntries = element.args().caller();
 +
if (!salesReport_DailyEntries)
 +
{
 +
throw error(Error::missingRecord(funcName()));
 +
}
 +
}
 +
An instance of the class SalesReport_DailyEntries is created. A check is made to
 +
validate whether the report is called from the classs salesReport_DailyEntries. This is
 +
done to prevent the report being executed directly from X++ or the AOT, so the logic
 +
from the class would not be called. In this example it would not matter from where the
 +
report was called. But it could be the case that the class was filtering data to be
 +
printed.
 +
As mentioned earlier the class SalesReport_DailyEntries is not needed as the
 +
runBaseReportStd class will handle the logic. Try changing the report not to use the
 +
class. The only change needed is to modify the init() method of the report. As init() is
 +
only initializing and doing validation for the class you can simple delete init(). The
 +
menu item is still referring to the class, so you will have to go to the output menu item
 +
SalesReport_DailyEntries and change the properties for the menu item so it calls the
 +
report instead. When executing the report you will get the same result as if you were
 +
using the class SalesReport_DailyEntries.
 +
Example 7: Report dialog
 +
Elements used from MorphxIt_Report project
 +
� Report, SalesDailyEntries
 +
� Menu item output, SalesDailyEntries_Without_Class
 +
Now it is time to add some more features to the SalesDailyEntries report. A dialog
 +
field for specifying whether details must be printed will be added. The value of the
 +
new field will be stored, so the last value is loaded when executing the report. The
 +
following must be added:
 +
public class ReportRun extends ObjectRun
 +
{
 +
DialogField dialogPrintDetails;
 +
Boolean printDetails;
 +
#DEFINE.CurrentVersion(1)
 +
#LOCALMACRO.CurrentList
 +
printDetails
 +
#ENDMACRO
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
25 of 40
 +
}
 +
DialogPrintDetails is a variable of the class DialogField and is used for the new dialog
 +
field. The variable printDetails stores the value of the dialog field. The macro
 +
CurrentList is a list of variables to be stored. The list will typically contain a variable
 +
for each field in the dialog. In this example CurrentList only contains a single
 +
variable, but it could be as many as needed, simply separate the variables by a comma
 +
to add more. CurrentVersion is keeping track of the stored version of CurrentList. If
 +
changes are made to CurrentList, then CurrentVersion must be incremented by one.
 +
public Object dialog(DialogRunbase _dialog = null)
 +
{
 +
DialogRunBase dialog;
 +
;
 +
dialog = super(_dialog);
 +
dialogPrintDetails = dialog.addFieldValue(typeId(NoYesId), printDetails, "Print details",
 +
"Print additional information for the transactions.");
 +
return dialog;
 +
}
 +
This method defines the dialog. The dialog is initiated from super() and then dialog
 +
will contain default dialog for the report. The only thing needed is to add the new field
 +
for printing details. The new field will automatically be put in a default field group
 +
called Parameters.
 +
public boolean getFromDialog()
 +
{
 +
boolean ret;
 +
printDetails = dialogPrintDetails.value();
 +
ret = true;
 +
return ret;
 +
}
 +
When Ok is pressed in the dialog this method is called. The value from the new dialog
 +
field is stored in the printDetails variable.
 +
public container pack()
 +
{
 +
return [#CurrentVersion, #CurrentList];
 +
}
 +
The last value from the new dialog field is stored. Pack() is saving the current value.
 +
CurrentVersion and CurrentList specified in ClassDesclaration are used as keys and
 +
field list.
 +
public boolean unpack(container packedClass)
 +
{
 +
boolean ret;
 +
Integer version = conPeek(packedClass,1);
 +
switch (version)
 +
{
 +
case #CurrentVersion:
 +
[version, #CurrentList] = packedClass;
 +
ret = true;
 +
break;
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
26 of 40
 +
default:
 +
ret = false;
 +
}
 +
return ret;
 +
}
 +
The method is loading the last value stored using CurrentVersion and CurrentList.
 +
public void run()
 +
{
 +
if (printDetails)
 +
{
 +
SalesLine_Name.visible(true);
 +
}
 +
else
 +
{
 +
SalesLine_Name.visible(false);
 +
}
 +
super();
 +
}
 +
The last step is to do the check whether details must be printed or not. In this example
 +
the difference will be whether SalesLine.name is printed or not. SalesLine_Name is
 +
declared by setting the property AutoDeclaration for the control SalesLine_Name to
 +
Yes. When a control is auto declared you will have access to change the properties for
 +
the control at runtime.
 +
I the report dialog example the methods pack() and unpack() were used. These are the
 +
methods used to store the last value of a dialog. If you need this functionality for your
 +
dialog, you can just copy the two methods from an existing class, and add
 +
CurrentVersion and CurrentList to the ClassDeclaration.
 +
Dynamic reports
 +
The ability of doing changes at runtime is very useful, as it gives the option of
 +
changing properties or adding new elements at runtime. This gives the option of
 +
creating one report, instead of having several reports with similar designs. The report
 +
SalesInvoice is an example of this, depending on the sales parameter settings
 +
SalesInvoice is printed with different controls visible.
 +
As Axapta is a multi language system, the use for printing reports in several languages
 +
is a must. Axapta will handle this alright. The report will by default be printed in the
 +
language Axapta is started up with. However if you print a formula like a sales
 +
invoice, the report must be printed in a customers preferred language. This is done by
 +
setting the property Language on the node ReportDesign. You can set the property to
 +
a fixed value, but the right way will be setting the language from X++. Here
 +
myTable.languageId is used to set the language for the design:
 +
public void init()
 +
{
 +
super();
 +
element.design().laguageId(myTable.languageId);
 +
}
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
27 of 40
 +
The keyword element is used within the report to reference all of the reports objects.
 +
As here the element is referring to the method languageId() in the object design. In
 +
this way you can get a handle on each element of the report. Using element to
 +
reference report object might result in a long path, consider this:
 +
element.design().sectionGroup(tablenum(CustInterestTrans)).section(ReportBlockType::BODY).contr
 +
olName('custInterestTrans_custInterestJourInterestAmount');
 +
The above line is from the method init() in the report CustInterestNote. A better way
 +
is to use the property AutoDeclaration. When using the AutoDeclaration property you
 +
are actually declaring an instance of a system classes. SalesLine_Name used in the
 +
report dialog example is an instance of the system class ReportStringControl.
 +
Note: When browsing the standard reports in the AOT, you may see reports where system classes for
 +
sections and controls have been declared manually from code, rather than using the AutoDeclaration
 +
property. First by v3.0 of Axapta the AutoDeclaration property was added to report controls.
 +
The system classes give the opportunity creating reports from scratch though it is not
 +
the intention. The system classes are meant to be used when you need to add elements
 +
to a report at runtime, like sections and controls. The case could be that you at runtime
 +
would print detailed information or print an extra section from a different table
 +
depending on where your report is called from. Instead of using the system classes
 +
creating the controls at runtime, you can just add all the sections and controls needed
 +
for the different combinations and then use the property visible. In some cases using
 +
the system classes would be preferable. By using system classes for creating controls,
 +
you can wait till runtime deciding the type of your control. This is neat if the controls
 +
you need to add depend on the user settings in a form.
 +
Example 8: Report system classes
 +
Elements used from MorphxIt_Report project
 +
� Report, MyReport_SystemClasses
 +
� Menu item output, MyReport_SystemClasses
 +
To try out the report system classes, create a new report as shown in figure 10, test of
 +
report system classes. The report will at runtime create a body section for CustTable.
 +
The body section will have 10 controls, printing the value of the first 10 fields from
 +
CustTable.
 +
The table CustTable is added a as data source and the node for the auto design has
 +
been created.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
28 of 40
 +
Figure 10, test of report system classes
 +
Now go and overload the init() method of the report. Init() will hold all the code for
 +
the report.
 +
public void init()
 +
{
 +
ReportSection reportSection;
 +
DictTable dictTable;
 +
Counter fieldCounter;
 +
super();
 +
reportSection = element.design().autoDesignSpecs().addSection(ReportBlockType::Body);
 +
reportSection.table(tableNum(custTable));
 +
dictTable = new DictTable(tableNum(custTable));
 +
while (fieldCounter < 10)
 +
{
 +
fieldCounter++;
 +
dictField = new DictField(dictTable.id(), dictTable.fieldCnt2Id(fieldCounter));
 +
reportSection.addControl(dictTable.id(), dictTable.fieldCnt2Id(fieldCounter));
 +
}
 +
}
 +
The instance of the system classes reportSection and reportControl is used to create
 +
the body section and the controls for the body section. A new report section of the
 +
type body section is added to the node AutoDesignSpecs specified to use the table
 +
CustTable. DictTable are also an instance of a system class. DictTable is often used
 +
when a handle is needed for table and field properties. Here dictTable is used to loop
 +
the first 10 fields in CustTable. For each loop a control is added to the body section.
 +
That is all. Morphx will handle adding the proper control type, and auto adjusting
 +
your controls, so when you run the report you will have the first 10 fields of the table
 +
CustTable printed in a row. If you are creating a module where the user must have the
 +
option of defining their own layout of a report, using the system classes could be the
 +
answer.
 +
Common report methods
 +
When doing modifications to a report you are overloading existing methods, or adding
 +
new methods which are called from an overloaded method. The following methods
 +
are executed in listed order when a report is loaded:
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
29 of 40
 +
init() � dialog() � run() � fetch() � send() � print()
 +
Init() and dialog() are triggered when the report is loaded. Run() is triggered when the
 +
button Ok is pressed in the dialog. Fetch() is looping the query and for each record
 +
found send() is triggered. At last print() is triggered. These methods are the most
 +
important methods of a report, and often it will be the methods you need to override.
 +
Common situations are when adding controls to a dialog or need to control the output
 +
from the query before it is printed like if a programmable section must be printed with
 +
a body section.
 +
The above listed execution order is when the report runbase framework is in use. If
 +
you call your report directly without using a menu item, the execution order of the
 +
methods is slightly different:
 +
init() � run() � prompt() � fetch() � send() � print()
 +
Dialog() will not be triggered. RunBaseReportStd is controlling the dialogs of the
 +
report, and when the runbase framework is not in use prompt() is used.
 +
Example 9: Overloading fetch()
 +
Elements used from MorphxIt_Report project
 +
� Report, MyReport_Fetch
 +
� Menu item output, MyReport_Fetch
 +
Now try doing an overload of fetch(). A new report will be created printing customer
 +
transactions for each customer, filtered from n-days till system date. The report will
 +
have a dialog where the n-days is keyed in by the user. Start by duplicating the
 +
MyReport example. The report will end up as shown in figure 11, report for
 +
overloading fetch(). The example will focus on overloading the methods.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
30 of 40
 +
Figure 11, report for overloading fetch()
 +
As you now have created the query and the design for the report, you are ready to
 +
create the methods for the report.
 +
public class ReportRun extends ObjectRun
 +
{
 +
DialogField dialogDaysBack;
 +
NumberOf daysBack;
 +
}
 +
The variable dialogDaysBack is needed for the dialog. The value will be stored in the
 +
variable daysBack.
 +
public Object dialog(Object _dialog)
 +
{
 +
DialogRunBase dialog;
 +
;
 +
dialog = super(_dialog);
 +
dialogDaysBack = dialog.addFieldValue(typeId(NumberOf), daysBack, "Number of days",
 +
"Number of days back to be printed.");
 +
return dialog;
 +
}
 +
No news here. A field is added to the dialog to enter the n-1 days.
 +
public boolean getFromDialog()
 +
{
 +
boolean ret;
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
31 of 40
 +
daysBack = dialogDaysBack.value();
 +
ret = true;
 +
return ret;
 +
}
 +
The variable daysBack is set to store the value from the dialog.
 +
public boolean fetch()
 +
{
 +
QueryRun qr;
 +
QueryBuildRange rangeTransDate;
 +
Boolean ret;
 +
qr = new QueryRun(element);
 +
rangeTransDate =
 +
element.query().dataSourceTable(tablenum(CustTrans)).addRange(fieldnum(CustTrans,
 +
transDate));
 +
rangeTransDate.value(queryRange(systemdateGet()-daysBack, systemDateGet()));
 +
rangeTransDate.status(RangeStatus::LOCKED);
 +
element.design().caption(strFmt("%1, %2", element.design().caption(),
 +
rangeTransDate.value()));
 +
if (qr.prompt() && element.prompt())
 +
{
 +
while (qr.next())
 +
{
 +
custTable = qr.get(tableNum(CustTable));
 +
custTrans = qr.get(tableNum(CustTrans));
 +
if (!custTable)
 +
{
 +
ret = false;
 +
break;
 +
}
 +
if (qr.changed(tableNum(custTable)))
 +
element.send(custTable, 1);
 +
if (qr.changed(tableNum(custTrans)))
 +
element.send(custTrans, 2);
 +
}
 +
ret = true;
 +
}
 +
else
 +
ret = false;
 +
return ret;
 +
}
 +
Here goes the essential part. The variable daysBack contain the value the user has
 +
keyed in. A range must be added to the query to filter the transaction date with n-1
 +
days to system date. A QueryRun object called qr is initialized with the active query
 +
of the report, and then a range for the customer transaction date is added to qr. The
 +
range is locked, so the user cannot change it. The transaction date range is added to
 +
the caption of the report.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
32 of 40
 +
Then the query is looped. The loop of the query and the print of the record is what the
 +
super() call in fetch() handles. Before the query is looped there is a check to see
 +
whether the dialogs for the query and the report is called, the two dialogs which
 +
runBaseReportStd wraps. For each loop the tables CustTable and CustTrans are
 +
initialized. If no records are found, the loop breaks and the report end. If a data source
 +
has changed a new record is found, and the record is printed using the send() method.
 +
Note the second parameter in the send method. The second parameter is defining the
 +
level of the record. CustTable is on the first level of the query, and CustTrans is on the
 +
second level. This is important, as if it is not set correctly, auto sums will not be
 +
printed.
 +
In the fetch example the query of the report was looped. The case could also be
 +
looping a while select statement or a combination of both. For each record looped in
 +
the query, you might want to select a record from a table which is not part of the
 +
query or even build a temporary table to be printed. All you have to do is for each
 +
record to be printed, the send() method must be called.
 +
If your reason for overloading fetch() is doing validation of which records to be
 +
printed it will be easier overloading the send() method instead:
 +
public boolean send(Common _cursor, int _level=1, boolean _triggerOffBody=TRUE, boolean
 +
_newPageBeforeBody=FALSE)
 +
{
 +
boolean ret;
 +
CustTrans custTrans;
 +
if (_cursor.tableId == custTrans.tableId)
 +
custTrans = _cursor;
 +
if (custTrans.transDate == systemDateGet())
 +
ret = super(_cursor, _level, _triggerOffBody, _newPageBeforeBody);
 +
return ret;
 +
}
 +
The send() method has the record to be printed as cursor. All you need to do is
 +
initializing your table. Here the table CustTrans is initialized if the cursor is a
 +
CustTrans record. Only customer transactions with transaction date equal to the
 +
system date will be printed.
 +
Adding query values can often be handled just by overloading the init() method,
 +
which is much easier as only a few lines of code is needed. The range added to the
 +
query in the fetch example was dependent on user interaction. The modification had
 +
to be done after the dialog was closed, so the code had to be put in fetch(). Before
 +
adding a range to a query, you should always do a check for whether the query
 +
already contains a range for the field, by using the QueryBuildRange.findRange()
 +
method. If two ranges are created for the same field, the ranges will be and'ed which
 +
may give you unexpected results. The user has an option in the report dialog to print
 +
ranges for a query, when printing to a printer. Both ranges added to the query data
 +
sources and the ones added from X++ will be printed, however if fetch() is overloaded
 +
this option will be disabled.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
33 of 40
 +
Special reports
 +
Till now this chapter has been focused on the basic steps in creating reports. To get an
 +
idea of the options within the Morphx languages this section will show examples on
 +
how to solve different cases on reports with special needs and how you can make your
 +
reports more users friendly.
 +
Execute report from X++
 +
Reports are normally activated from a menu item, called from either the main menu or
 +
from a form. Sometimes it can be necessary to call the report directly from X++ as the
 +
user for some reason may not call the report directly from a menu item.
 +
Elements used from MorphxIt_Report project
 +
� Job, ExecuteReport
 +
� Job, ExecuteReportSilent
 +
static void ExecuteReport(Args _args)
 +
{
 +
Args args;
 +
SysReportRun reportRun;
 +
;
 +
args = new Args();
 +
reportRun = new menuFunction(menuItemOutputStr(MyReport),
 +
MenuItemType::Output).create(args);
 +
reportRun.run();
 +
}
 +
The job shows how MyReport is called from X++. Notice the application class
 +
SysReportRun is used. SysReportRun is inherited from the system class ReportRun.
 +
The benefit of using the application class is that you have the option to override the
 +
code in the SysReportRun class. You could also create your own extended class
 +
inherited from the SysReportRun class. This could be the case if you have a series of
 +
reports where checks must be made when printing, rather than modifying each single
 +
report.
 +
MyReport is called using the menu item as this will trigger the runbase report
 +
framework and having the correct dialog for the report loaded. If you do not want to
 +
use the runbase framework, or you want to print the report without user interaction
 +
you must instead trigger the report without using the runbase report framework.
 +
static void ExecuteReportSilent(Args _args)
 +
{
 +
Args args;
 +
SysReportRun reportRun;
 +
;
 +
args = new Args();
 +
args.name(reportStr(MyReport));
 +
reportRun = new SysReportRun(args);
 +
reportRun.query().interactive(false);
 +
reportRun.report().interactive(false);
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
34 of 40
 +
reportRun.setTarget(PrintMedium::Printer);
 +
reportRun.run();
 +
}
 +
In this job the report is executed without using the menu item and then the runbase
 +
report framework is not used. The example prints the report direct to the default
 +
printer without user interaction as both the query and the report dialog are set to
 +
inactive. If dialog() is overridden in your report, you must assure that this do not give
 +
any problems, as dialog() will not be executed.
 +
If your report consists of more than one design you must specify which design to be
 +
used. If not specified or if an invalid design name is entered, the first design for the
 +
report will be used
 +
reportRun.design("MyDesign");
 +
reportRun.run();
 +
Using temporary tables
 +
If a special sorting is needed, or you must select data from several tables which cannot
 +
be joined, using a temporary table can be the answer. Using temporary tables is in fact
 +
pretty simple. The temporary table must just be filled and passed on to the report.
 +
There will be a performance issue when using temporary tables, as the report will
 +
have two runs. First the temporary table is build, and second the temporary table is
 +
looped in the report. The use of temporary tables should not be your first choice. You
 +
might be better off reconsidering your design. For more on temporary tables see the
 +
chapter Data Dictionary.
 +
Elements used from MorphxIt_Report project
 +
� Classs, Reports_TempTable
 +
� Report, Reports_TempTable
 +
When using temporary tables you should have the report called from a class. This will
 +
give you the option building the temporary table at sever side. The following example
 +
shows how to create a report using a temporary table. The example is for simplicity
 +
just adding 10 records to a temporary table and printing the result.
 +
class Reports_TempTable extends runBaseReport
 +
{
 +
}
 +
The class is inherited from runBaseReport.
 +
public identifiername lastValueElementName()
 +
{
 +
return reportStr(Reports_TempTable);
 +
}
 +
The name for the report is specified.
 +
tmpAccountSum tempTable()
 +
{
 +
CustTrans custTrans;
 +
TmpAccountSum tmpAccountSum;
 +
Counter counter;
 +
;
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
35 of 40
 +
while select custTrans
 +
{
 +
counter++;
 +
if (counter == 10)
 +
break;
 +
tmpAccountSum.accountNum = custTrans.accountNum;
 +
tmpAccountSum.currencyCode = custTrans.currencyCode;
 +
tmpAccountSum.balance01 = custTrans.amountMST;
 +
tmpAccountSum.insert();
 +
}
 +
return tmpAccountSum;
 +
}
 +
The temporary table tmpAccountSum is used. The first 10 records from the table
 +
CustTrans are inserted in tmpAccountSum. This method is used by the report to pass
 +
on the buffer for the temporary table to the report.
 +
static void main(Args args)
 +
{
 +
Reports_TempTable reports_TempTable = new reports_TempTable();
 +
if (reports_TempTable.prompt())
 +
reports_TempTable.run();
 +
}
 +
The class is initialized and the report is executed.
 +
Figure 12, Report using temporary table
 +
Last step is to create the report. A report with the temporary table TmpAccountSum as
 +
data source must be created. The 3 fields filled out with values from CustTrans will be
 +
printed. Your report must look like figure 12, report using temporary table.
 +
public void init()
 +
{
 +
Reports_TempTable reports_tempTable;
 +
;
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
36 of 40
 +
super();
 +
reports_TempTable = element.args().caller();
 +
if (!reports_TempTable)
 +
throw error(Error::missingRecord(funcName()));
 +
reports_TempTable.queryRun().setRecord(reports_TempTable.tempTable());
 +
}
 +
Init() must be overloaded. The runbase class is initialized and the query is set with a
 +
buffer to the temporary table. Notice you will just have to set a reference to the buffer.
 +
The query will then have the full scope of the temporary table, and loop all of the
 +
records in the temporary table.
 +
Coloring rows
 +
Colors are rare in the reports in the standard packing. The reason might be that you
 +
will have to fiddle a bit with the report to have a proper result. However the use of
 +
colors can give your report the final touch and even ease up reading the printout.
 +
Elements used from MorphxIt_Report project
 +
� Report, MyReport_Color
 +
� Menu item output, MyReport_Color
 +
This example will show how to color a single column based on a condition. The
 +
report will set the background color the control printing CustTrans.amountMST. To
 +
simplify the condition check is done from X++. In real life the conditions could be set
 +
up in a dialog or based on data in a form. You should end up with a design as shown
 +
in figure 13, report coloring rows.
 +
Figure 13, report coloring rows
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
37 of 40
 +
1. Start out by duplicating the report MyReport, and rename it to MyReport_Color.
 +
2. The goal is to color the control CustTrans_AmountMST. By using the MyReport
 +
example as it is the header label will also change color. Instead of the standard
 +
header for the body section CustTrans_Body, a new one must be created. To skip
 +
the standard header set the property NoOfHeadingLines to 0 on the body section
 +
CustTrans_Body.
 +
3. Create a new header by adding a programmable section and add a prompt control
 +
for each of the 3 fields in the body section. The property ModelFieldName on
 +
each prompt control must be set to the corresponding body section control name.
 +
Add a label for each prompt control.
 +
4. Declare a variable to keep track of when the programmable section used for
 +
header must be printed.
 +
public class ReportRun extends ObjectRun
 +
{
 +
Boolean printCustTransHeader;
 +
}
 +
5. Now overload send(). If the current record is a CustTrans record the header is
 +
printed for the first CustTrans in a row. A condition is set up for
 +
CustTrans.AmountMST. If an amount larger than 500 is printed, the background
 +
color is set to yellow. Otherwise the background color will be neutral.
 +
public boolean send(Common _cursor, int _level=1, boolean _triggerOffBody=TRUE, boolean
 +
_newPageBeforeBody=FALSE)
 +
{
 +
boolean ret;
 +
;
 +
if (_cursor.tableId == tableNum(custTable))
 +
printCustTransHeader = true;
 +
if (_cursor.tableId == tableNum(custTrans))
 +
{
 +
if (printCustTransHeader)
 +
{
 +
element.execute(10);
 +
printCustTransHeader = false;
 +
}
 +
if (custTrans.amountMST > 500)
 +
CustTrans_AmountMST.backgroundColor(Winapi::RGB2int(255, 255, 0));
 +
else
 +
CustTrans_AmountMST.backgroundColor(Winapi::RGB2int(255, 255, 255));
 +
}
 +
ret = super(_cursor, _level, _triggerOffBody, _newPageBeforeBody);
 +
return ret;
 +
}
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
38 of 40
 +
Print using Word
 +
Creating reports in Axapta with a complex design such as a formula with boxes, tables
 +
and graphic can be a battle. You sure can do it but, but it might be an idea trying out
 +
other tools. By using the com interface you can connect to external applications like
 +
Word. To use Word for printing data you must create a Word template with
 +
bookmarks. The bookmarks are used to position the data from Axapta. Notice, that
 +
you must have a license code for at least one com client to use the com interface.
 +
Note: The Document handling in the standard package uses the com interface to attach files from Excel
 +
and Word. The classes used by Document handling are prefixed with DocuActionCOM.
 +
Elements used from MorphxIt_Report project
 +
� Class, PrintUsingWord
 +
Additional
 +
� Word template, Reports_WordTemplate.dot
 +
This example will show how to connect to Word and create a new document printing
 +
data from the table InventTable. The first 10 records from InventTable will be printed.
 +
Labels will be printed for header and column headers.
 +
You will have to start creating a Word template, with the following bookmarks:
 +
label_header, label_itemid, label_itemname, and label_itemdesc. Label_header will
 +
print a heading text for the column. Create a table and add the remaining 3 bookmarks
 +
as header for the table. The next step is to create the following class:
 +
void run()
 +
{
 +
COM COMAppl, COMDocuments, COMDocument;
 +
;
 +
COMAppl = new COM('Word.Application');
 +
COMDocuments = COMAppl.documents();
 +
// enter path to the template Reports_wordtemplate.dot
 +
COMDocument = COMdocuments.add('d:\\Reports_WordTemplate.dot');
 +
if (COMDocument)
 +
{
 +
this.setLabels(COMDocument);
 +
this.sendInventTable(COMDocument);
 +
this.showDocument(COMAppl);
 +
}
 +
}
 +
Run() will initiate the COM connection, and open a new Word document based on the
 +
template Reports_WordTemplate.dot. Remember to check that the path for the
 +
template is valid. If the document is created, labels and data will be added. Finally the
 +
document will be shown. Note that the document shown is not saved. If you want to
 +
save the document you must add: COMdocument.saveAs(<filename>,0,false,'',false);.
 +
void setLabels(COM _COMDocument)
 +
{
 +
COM COMBookmarks, COMBookmark, COMrange;
 +
DictField dictField;
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
39 of 40
 +
Label label;
 +
;
 +
COMBookmarks = _COMDocument.bookmarks();
 +
if (COMbookmarks.exists('label_header'))
 +
{
 +
COMbookmark = COMbookmarks.item('label_header');
 +
COMrange = COMbookmark.range();
 +
COMRange.InsertAfter("Inventory list");
 +
}
 +
if (COMbookmarks.exists('label_itemId'))
 +
{
 +
COMbookmark = COMbookmarks.item('label_itemId');
 +
COMrange = COMbookmark.range();
 +
DictField = new dictField(tableNum(inventTable), fieldNum(inventTable, itemId));
 +
COMRange.InsertAfter(dictField.label());
 +
}
 +
if (COMbookmarks.exists('label_itemName'))
 +
{
 +
COMbookmark = COMbookmarks.item('label_itemName');
 +
COMrange = COMbookmark.range();
 +
DictField = new dictField(tableNum(inventTable),
 +
fieldNum(inventTable, itemName));
 +
COMRange.InsertAfter(dictField.label());
 +
}
 +
if (COMbookmarks.exists('label_itemDesc'))
 +
{
 +
COMbookmark = COMbookmarks.item('label_itemDesc');
 +
COMrange = COMbookmark.range();
 +
label = new Label(CompanyInfo::languageId());
 +
COMRange.InsertAfter(label.extractString(literalstr("@SYS58702")));
 +
}
 +
}
 +
A check is made to see whether the bookmark can be found. If found the labels are
 +
set. The header label is set with the static text 'Inventory list'. The labels for the
 +
bookmarks label_itemId and label_itemName are set with the label for the
 +
corresponding table fields. The label for the bookmark label_itemDesc is set using the
 +
method label.extractString() to fetch the label for the default company language.
 +
void sendInventTable(COM _COMDocument)
 +
{
 +
COM COMTable, COMRows, COMRow;
 +
COM COMCells, COMCell, COMRange;
 +
InventTable inventTable;
 +
Counter counter;
 +
;
 +
//init tabel
 +
COMTable = COMDocument.Tables();
 +
COMTable = COMTable.Item(1);
 +
COMRows = COMTable.Rows();
 +
while select inventTable
 +
{
 +
counter++;
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com
 +
MORPHX IT Reports
 +
© 2005 Steen Andreasen
 +
40 of 40
 +
if (counter == 10)
 +
break;
 +
// add new row
 +
COMRow = COMRows.Add();
 +
COMCells = COMRow.Cells();
 +
// item id
 +
COMCell = COMCells.Item(1);
 +
COMRange = COMCell.Range();
 +
COMRange.InsertAfter(inventTable.itemId);
 +
// item name
 +
COMCell = COMCells.Item(2);
 +
COMRange = COMCell.Range();
 +
COMRange.InsertAfter(inventTable.itemName);
 +
// item description
 +
COMCell = COMCells.Item(3);
 +
COMRange = COMCell.Range();
 +
COMRange.InsertAfter(inventTable.itemDescription());
 +
}
 +
}
 +
Here the inventTable is looped. The table in Word is first initiated. For each loop a
 +
new row is added to the table in Word. The fields itemId, itemName and the display
 +
method itemDescription() from InvenTable is added to the 3 rows in the Word table.
 +
Notice that no bookmarks are needed
 +
void showDocument(COM _COMAppl)
 +
{
 +
_COMAppl.visible(TRUE);
 +
}
 +
The created Word document is shown.
 +
static void main(Args _args)
 +
{
 +
PrintUsingWord printUsingWord = new PrintUsingWord();
 +
;
 +
PrintUsingWord.run();
 +
}
 +
No news here. Just initiating and executing the class.
 +
1.4 Summary
 +
This chapter introduced you to reports in Axapta. The chapter has covered the basic in
 +
creating reports. By now you should be familiar with the single elements of a report,
 +
such as data sources, designs, sections and controls in designs and the common
 +
methods on a report. You should have acquired knowledge on how to create reports
 +
using the report generator, and hopefully you have got an insight in the possibilities
 +
with reports in Axapta.
 +
MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com

Revision as of 07:15, 1 November 2005

Report development category.

This category contains articles pertaining specifically to development issues and best practice of Axapta reports.

ItMORPHX T An introduction to Axapta X++ and the Morphx Development Suite © 2005 Steen Andreasen MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com About this pre-release This is a pre-release of the chapter Reports from the upcoming Axapta X++ and Morphx book, MORPHX IT. The book MORPHX IT is expected to be published at January 2006. This chapter is covering how to use the report generator in Axapta. Besides this chapter on reports, the final book will have a report appendix chapter consisting of a step-by-step guide to the report wizard, description of the properties on a report and a description of each report system class. All code examples will be available for download with the final book. As the chapter is taken out of context you might be missing other parts from the book, such as the introduction chapter to X++ and Morphx and the report appendix chapter. The book is written as a practical book, you will be learning by doing. To gain the full benefit of the content you will have to do the examples in Axapta while reading the chapter. Parts of the chapter are still subject to change, however this chapter will give you an idea of what to expect for the final book. Warning You should never try out any of the examples in this chapter in a live environment. The information in this chapter is provided as is. I or steenandreasen.com cannot be responsible of any loss or damages arisen from the information containing in this chapter. Trademarks Axapta, Morphx, IntelliMorph, X++ are trademarks of Microsoft Corp. Other company names and product names can be trademarks belonging to their respective owners. Copyright Copyright 2005, steenandreasen.com. All rights reserved. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com Table of contents 1 REPORTS.............................................................................................4 1.1 Report wizard..................................................................................................4 1.2 Report generator ............................................................................................4 Report query.....................................................................................................7 Templates .........................................................................................................9 Designs...........................................................................................................12 1.3 Methods on a report .....................................................................................19 Report runbase framework .............................................................................22 Dynamic reports .............................................................................................26 Common report methods................................................................................28 Special reports................................................................................................33 1.4 Summary .......................................................................................................40 MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 4 of 40 1 Reports Reports in Axapta are based on a query and a design stored in the AOT. A report contains no data. The print of a report is a result of the run-time fetch of the data. This means reports must always be printed having access to the database. When executing the report you have the option of printing the report immediately, or defining a batch job for executing the report at a later defined time. Batch processing is normally used for heavy reports like printing monthly customer balance lists. Printing reports from Axapta is very flexible, as Morphx provide tools for overriding the predefined report. By using the Morphx environment you can at runtime filter data, or even modify the layout of the printed job. There are two ways of creating reports in Axapta. Either by using the built-in report wizard, or by using the report generator, located in the AOT under Reports. Explanation of the single options in the report dialog is out of scope for this chapter. The chapters focus is on the technical part of reports. You do not need to have knowledge about the report end user interface to get benefit of this chapter, though it will be helpful. If you are not familiar with the end user interface for reports you can get more detailed information by checking the manuals in the standard package. 1.1 Report wizard The report wizard is a neat tool for non-technical persons to create reports. The wizard is located in the toolbar menu Tools | Development tools | Wizards | Report wizard. This would be the place to start out learning about Axapta reports. The report wizard is an end user tool, which guide you through the steps of creating reports. You have the option of storing a report created by the wizard in the AOT. This makes the wizard interesting as it will help you getting familiar with the elements of an Axapta report. Use the wizard for creating the basic structure of your report. The last modifications can then be done by using the report generator to edit the stored report. For a step-by-step guide on how to use the report wizard, see the appendix Reports appendix. 1.2 Report generator By checking out what the report wizard generates, you will soon learn the basics needed for creating your reports from scratch using the report generator. When starting out creating a new report it can often be an advantage looking at some of the standard reports in the AOT, before starting creating your own. Find an existing report matching some of your needs. Simply duplicate an existing report and start doing your modification to the copy of the report. For locating a report from the menu see the chapter Intro to X++ and Morphx. When starting out with the report generator the tutorial reports in the standard package might be useful. Take a look at the reports prefixed with tutorial_. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 5 of 40 Axapta reports are divided into two parts, the Data Sources node where the data to be fetched is defined and the Designs node where the presentation of the data is defined. For an overview of a report see figure 1, report overview. Note: It is possible to call another report from within a report however, if you miss this feature you must use X++ instead. Figure 1, report overview Example 1: My first report Elements used from MorphxIt_Report project � Report, MyReport � Menu item output, MyReport Before going into details, start creating a report as seen in figure 1, report overview. The report is printing customer transactions grouped by customer. The example is kept simple as the focus is to learn the basic moves building a report. Later in this chapter, the details will be explained and more features will be added to the example. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 6 of 40 1. Create a new report by right-clicking the Reports node in the AOT and choose New Report. A new report called "Report1" will be created. Open the property sheet and rename the report "MyReport". 2. Two levels of data sources are needed for the query, the customer tables as the first level and the customer transaction table as the second level. Open another instance of the AOT and drill down the Data Dictionary/Tables node. Pick the table CustTable. Unfold the Data Sources/Query node in your report, and drag CustTable to the Data Sources/Query/Data Sources node. Now the first level of the query has been added. Unfold the CustTable data sources node in your report. Drag the table CustTrans to the CustTable/Data Sources node. 3. The two data sources of the query must be linked. Otherwise all customer transaction will be printed for each customer. Go to the CustTrans node in the Query and set the property Relations to Yes. The node CustTrans/Relations will now contain an entry linking the two tables. 4. The query part is now ready to fetch data for the report, and the presentation of data must now be done. Go to the Designs node, right-click and choose New Report Design. A new design called "ReportDesign1" will be created. Go to the ReportDesign1 node and enter the text "Customer transactions list" in the Caption property. 5. Go to the ReportDesign1/AutoDesignSpecs node. Right-click the node and choose Generate Specs From Query. Your design will now contain two body sections, one for each table in the query. 6. The last step is to pick the fields to be printed. Right-click the Query node of your report, and choose Open New Window. This will ease up dragging the fields to be printed to the design. Pick the fields AccountNum and Name from the CustTable data source and drag the fields one at the time to the body section node CustTable_Body. Go to the CustTrans data source and drag the fields Voucher, TransDate and AmountMST to the body section CustTrans_Body. 7. You now have a report as seen in figure 1, report overview. Try executing the report by right-clicking on the report name and choose Open. A dialog for filtering, sorting and other print options will show. For now just click Ok. The next dialog is the printer dialog. Check the printout is set to screen and click Ok. Your report will now be printed to the screen. 8. As you might have noticed, the layout has still some finish to be done. To simplify the example just add a report template to format the heading of the report. Go to the Designs/reportDesign1 node and locate the property ReportTemplate. Click the arrow and choose the template InternalList. 9. Run the report again by following step 7. Now your report has been added heading information like name of the report, page number, date and time. That is it. You have created your first report! MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 7 of 40 In the MyReport example you did not have to write a single line of code. When creating reports in Axapta, you will not have to worry about writing code for data connections and position controls in the layout. Morphx will handle this for you. Simple create a query and select the column order of your controls in the design. Only when creating more advanced reports in Axapta like where fetching data is to complex to fix in a query, or a special design is needed, you will have to use X++. When you executed the report, two dialogs were shown. Try creating a menu item for MyReport by dragging MyReport to Menu Items/Output node. You can now execute your report by right clicking the new menu items and choose Open. The report will now have only one dialog. This is the dialog the users will see, further details on this later in the chapter. For an overview of properties for the single elements in a report see the appendix Reports appendix. Report query Fetching data for a report is usually done using a query. In the query the data sources used for the report and the relation between the data sources are defined. It is optional using a query, as there can be situations where a query cannot fulfill the needs, and you will have to fetch your data by using X++. The report generator is using a standard Axapta query. For more information on building a query, see the chapter Queries. Before building your query, you will have to decide which tables to be used. The typical situation will be that you have data from a single form or related forms that you want to print, as seen in the MyReport example. For help on how to locate tables and fields from the forms, see the chapter Intro to X++ and Morphx. When you have localized the tables to be used you must, before adding the tables as data source to the report query, decide how the sorting and the filtering of the report must be. This is a performance issue as you will have a must faster report if you have done some thoughts of this before carrying on. If you are to traverse a lot of inventory transactions filtering of data at the third level of a query might not be a good idea. A rule of thumb can be, that if your report is filtering data at the third level of the query, your design might be wrong. What you are trying to accomplish might have to be two reports rather than one, or you should consider using a temporary table to build the result to be printed. The common situation will be to add all tables needed for the report to the Query node. If data from related tables are used, the tables must be joined and added at each level in the query as done with CustTable and CustTrans in the MyReport example. If you are fetching data from more tables which there are no direct relations in between, but still the printout of the second level table is dependent on the first level table, you will either have to add all tables to the query, or just add the first level table as a data source and use X++ for fetching the second level. Say you want to print sales invoice lines group by customer. There is no direct relation between the customer table and the sales invoice lines table, so the table sales invoice journal must be used to build the query, see figure 2, relation between CustTable and CustInvoiceTrans. The MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 8 of 40 recommended solution will be adding all 3 tables as data sources, as this give the user the full benefit of the report dialog. CustTable Customers 77 dat InvoiceAccount CustInvoiceJour Customer invoice journal 62 dat +CustInvoiceJour CustInvoiceTr ans Customer invoice lines 64 dat Figure 2, relation between CustTable and CustInvoiceTrans Joining data sources can be done in two ways. If the data sources already have a relation, the property Relation on the lower level data source must be set to true, just as in the MyReport example. The relation will then be visible under the Relations node for the joined data source. If no relation shows up you must manually create the relation under the Relations node, the property Relation must then be set to false. It is recommend using an already existing relation, rather than manually creating your own, as changes to the data dictionary would then automatically reflect the report. The data sources will by default be joined using inner join, but the join mode can be changed on the properties for the joined data source. Inner joins are the most frequently used for reports, as the common case is that you have data in a main table and want to print the related transactions. However if you want to print all records from the main table even if there are no transactions, you will have to change the join mode to OuterJoin on the transaction table. By right-clicking the Fields node you can choose to add a field or an aggregated function. By default all the fields from the current table are listed and so it will make no sense adding additional fields from the table. If you add an aggregate function, all the fields will be removed, as you cannot use both. To remove the aggregate functions and restore the field list, change the Fields node property Dynamic to Yes. The aggregate functions can be used if you want to count the number of customers group by customer group. You will have to select a table, choose an aggregate function and fields to be used. Example 2: Aggregate function Elements used from MorphxIt_Report project � Report, MyReport_aggregate � Menu item output, MyReport_aggregate In the following example a count of customer by customer group will be made. The design is simplified to focus on the aggregate functions. 1. Add the customer table to the report query, then go to the Fields node right-click and choose the aggregate function Count. The count field must be AccountNum. 2. Go to the CustTable data source and set the property OrderMode to Group by. Selecting data is always default done using order by. The last step is adding the fields to be sort by. Go to the Sorting node and add the field CustGroup. You will now have a query as seen in figure 3, aggregate function. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 9 of 40 Figure 3, aggregate function 3. Now create a design to print the result. Create an auto design and choose Generate Specs From Query as done in the MyReport example. You will now have a body section for CustTable with one control printing the field CustGroup. Add the field AccountNum. 4. Run the report. A row will be printed for each customer group. The AccountNum field will count the number of customers in each customer group. When using aggregate functions data must be selected using group by. The compiler will give an error if trying to select order by. This makes sense as data are selected record by record when using order by. Using group by records will be group by the sorting fields. This means only fields added as sorting fields will contain a value when using group by. You can add as many aggregate functions as needed. The case could be that you want to print a transaction list with an aggregation for min, max and average amounts. The Sorting node under the Data Sources node is used for setting the sorting of the report. This can be done either by using indexes or choosing fields. At least one index or sorting field should be defined. Fields chosen for sorting can at runtime be changed by the user. Keep in mind that the use of a field for sorting rather than an index may slow down your report. The sorting fields added, have a property called AutoSum, this is used if you want Morphx to print sub totals when the value of the field changes. In the section Auto design further details on using auto sums will be explained. The default ranges are specified using the Range node under the data source node. The user can at runtime add additional ranges or remove the default ranges depending on the property settings for the range. You can specify a default value for a range, and if the user may not change the range, the range can be locked or hidden from the properties. If no ranges have been specified, the first element of each index for the table will be used as default ranges at runtime. Try executing the report MyReport. You will see a default set of ranges has been added. Now go back and add the fields AccountNum to Data sources/CustTable/Ranges node. When executing MyReport only the range AccountNum will be listed. Templates In Axapta you have two different types of templates, report templates and section templates. The templates are located as the two first entries under the Report node in the AOT. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 10 of 40 Report template For the basic formatting of you reports like printing header and footer information, a report templates will be the right choice. You can use templates for more advanced cases like using data from specific tables, but it will make your templates less generic. Report templates are usually used for information not related to a specific table, like caption, page numbering and lines. The template InternalList used in the MyReport example is a commonly used report template, which is formatting the caption name, setting company name, page number, initials, date and time. You can create report templates with controls using a specific table. Just keep in mind that your tables must be declared and fetched in your report. To view the template, localize the template in the AOT, open the visual editor by right-clicking the template node and choose Edit. Example 3: Report template Elements used from MorphxIt_Report project � Report template, MyInternalList � Report, MyReport_MyInternalList Create a new template based on the InternalList template. InternalList contains basic header formatting. A prolog and an epilog section will be added to the new template. The new report template will be used to extend the MyReport example. 1. Start duplicating MyReport and rename the new report to MyReport_MyInternalList. 2. Go and locate the report template InternalList in the AOT. Right-click the report template and choose Duplicate. Rename the new report template to 'MyInternalList'. 3. Right-click the report template name, choose New and select the report section Prolog. The prolog will contain a text and a new page feed. First the text to be printed must be defined. Go to Prolog/Methods, right-click and choose New Method. Open the new method and enter the following: display description prologDescription() { return strFmt("Start of report: %1", element.design().lookupCaption()); } The method must now be used in the design for the prolog. Close the editor and drag the method to the Prolog node. A string control which will return the value of the display method prologDescription() has now been created. 4. Right-click the report template name, choose New and select the report section Epilog. Now created a method as in following, and drag the method to the Epilog node. display description epilogDescription() { return strFmt("End of report: %1", element.design().lookupCaption()); } MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 11 of 40 5. To have the prolog and epilog sections printed on new pages a new page feed must be added. Go to Prolog/Methods, right-click and choose Override Method and select executeSection(). Add the following to the method: public void executeSection() { super(); element.newPage(); } 6. Add a new page feed to the epilog section. The new page feed must be executed before super() in the epilog section, as the epilog must be printed on a new page. You will now have a report template as seen in figure 4, report template. Figure 4, report template 7. Next step is to use the new report template in MyReport_MyInternalList. Go to the node Designs/ReportDesign1, open the property sheet and select MyInternalTemplate as the report template. 8. Create a new menu item for the report. When the report is executed a page for the prolog will be printed before the report, and a page for the epilog will be printed after the report. The MyInternalList template uses display methods for returning the values for the controls. As for forms, display methods are often used for reports. This is a common way of printing data which is not part of a query. It could as here be text, or it could be a calculation, which is not suited to be a part of the query. Simple create your display method and drag the method to the design. You do not have to worry about the type of the control to display the value appropriate. Morphx will handle this for you by checking the methods return type. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 12 of 40 You ought to consider having one or two report templates, to be used for your reports. The advantage of using a report template for the basic formatting is to have identical presentation of your reports in an easy way. If you later on decide to change a report template, all reports with auto designs using the report template will automatically be changed. Section template The section templates were introduced in version 3.0. This could be the reason why they are rarely used and that you might not find any examples of their use in the standard package. A section template is using a table map as offset. Table maps are explained in the chapter Data Dictionary. Fields from the map can then be added as controls. In cases where you have reports with similar section blocks, the use of a section template could be the answer, rather than building the same block section in two different reports. In practice you might want to create one report and handle the control of your report using X++, rather than having two reports and using a section template. The SalesInvoice report is and example of this. Designs Positioning fields and controls in your design are normally handled by IntelliMorph. All controls will by default be auto adjusted as much as possible. This means that the controls will be set to auto positioning and fonts and font size is defaulted from the user options in the toolbar menu Tools | Options in the tab page Fonts. When you have chosen the row order Axapta will be positioning the controls based on the information from the extended data types. This is very helpful, as if you want to add a control in the middle of a row, or you want to hide a control, the following controls will be positioned accordingly. It is recommended to auto position controls as you get the full benefit of IntelliMorph, however in situations where your controls must always have a fixed position as formulas you can override the default settings. The disadvantage is that if you set a single control in a row to a fix position, you will have to define fixed positions for all controls. This cannot be recommended unless your controls must fit the layout of say a formula. If you are to print controls which must be positioned below each other in the same column you should consider using the property ModelFieldName which all controls on a report have. The current control will adjust to the name of the control specified in ModelFieldName, if the current controls positioning is set to auto. Creating design A report can have more than one design. Under the Design node, you can create as many designs as needed. This can be useful if you have a formula you want to print with different layout for each language or group of customers. More designs in one report are not that often used anymore in the standard package. Instead of having several designs the need for different layouts is handled by X++, see the report SalesInvoice. In the SalesInvoice report, the method element.changeDesign() is handling whether a control is to be printed or not. The benefit of always using a single design is that it is more time consuming maintaining differences in the design rather MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 13 of 40 than doing it from X++, like the SalesInvoice report, maintaining header sections in several designs is hard work as it will take time to locate and verify that your changes are identical in all designs. Note: If you are creating a report like a formula which must fit a preprinted layout it can be necessary to do the final adjustment using the specific printer driver, as the layout will be adjusted according to the single printer driver. Designs can be created either as auto design or as a generated design. A design can also consist of both an auto design and a generated design. In this case only the generated design will be used. The main differences between auto designs and generated designs are that auto designs give the opportunity for using dynamic templates, auto headers and auto sums. Generated designs are static. In other words, you will get the full benefit of using auto designs, as you are using the power of IntelliMorph. It is recommended using auto designs, only in special case where a fix layout is needed, like layout of a cheque or external formulas, generated designs can be an advantage. Generated designs have some extra sections for adding headers and footers to body sections. Beside that auto designs and generated designs are using the same type of sections. See figure 5, report design sections for an overview of sections in a report design. Type Description Prolog This is the first section printed. The prolog is typically used for printing a logo or a title on the first page. Page Header The page header is printed at the top of each page. A report can have more than one page header. Body The body section is printed after the page header. This is the data section. The report will normally contain a body section for each data source. Page Footer Page footer is printed at the bottom of each page. A report can have more than one page footer. Epilog This is the last page printed. Programmable Section Programmable sections are executed from code. This type of sections can be used in cases, where you need to print data which is not part of the query. Section Template Section Templates is used for defining common used data, typically used in body sections. A section is based on a Map. Header Header is used in Generated Designs as body header. Section Group In Generated Designs, the Body section is added to a Section Group. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 14 of 40 Footer Footer is used in Generated Designs as footer for a body section. Figure 5, report design sections You have two options for adding controls to your design, either by using the nodes in the report tree as shown in previous examples, or by using the visual editor. The visual editor gives the option of either viewing or editing the controls in your design. To edit double-click the design node, and to view right-click the design node and choose View. The visual editor has a view close to the printed result. If a report has a complex design like the SalesInvoice report, it can be difficult to figure out how the result will look on print. For more simple report, the editor is at better choice. You have the same features in the visual editor as if you are using the AOT for creating your design. From the visual editor you can change the properties for an element of the report and add or delete controls. To modify the report from the editor, simple position the cursor and right-click for the menu to edit, delete or add an element. To change the unit for the ruler right-click and choose between centimeters, inches or chars. Figure 6, The visual editor This sounds all very well, but in practice the visual editor is best for just getting an overview of your design or to locate and change the properties for the control. The visual editor is acting slowly. Try opening the property sheet on top of the visual editor and drag the property sheet around. All contest of the editor is redrawn. This is just not good enough. Do not waste too much time on this. Auto design The most common way of creating the layout for your reports is by using auto designs. When using auto designs, you will only have to choose a report template and the fields to be printed from the query. IntelliMorph will handle formatting the layout. If your report contains integer or real fields, the user will at runtime have the option of choosing summarization. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 15 of 40 For a quick start creating your auto design, it can be an advantage right-clicking the auto design node and choose Generate Specs From Query, a body section will then be created for each data source in the query, and the sorting fields will be added as controls to the design. To get a visual view of your report, right click the auto design node and select View. Try opening MyReport in the visual editor. Notice that the visual editor will show the sections from the report template, even though the template sections are not a part of the reports nodes. This is pretty neat, and it helps give an overview of the report. To edit the report right click and choose edit. In edit mode only the nodes which are part of the report are accessible. The use of auto sums is a useful feature in auto designs, as the end user can define the levels to be summed at runtime. From the report dialog you can set break levels for sums for any field or body section and finally you can set a total for the whole report. From an end users point of view it might be the most important reason for using auto designs. It makes your report more flexible, and at last it might save you time not having to hardcode any sums. Example 4: Auto sum Elements used from MorphxIt_Report project � Report, MyReport_Sums � Menu item output, MyReport_Sums Extend MyReport with totals for the transaction amounts. A sub total for each customer and a total for all customers will be added. 1. Start duplicating MyReport and rename the new report to MyReport_Sums 2. As an index does not have an option setting break points for totals, the index AccountIdx will be removed from the CustTable data source, and the fields AccountNum will be added instead. Set the property AutoSum to Yes for the field AccountNum. You have now defined that each time the value of a customer account changes a sub total will be printed. 3. The fields to sum up must be defined. In this example only the fields AmountMST from the table CustTrans will be used. Find the control printing AmountMST in the CustTrans body section, open the property sheet and set SumAll to Yes. 4. Create a new menu item for the report, and run the report. For each customer a sub total will be printed. 5. As you notice only a sub total was printed. To add a total for the whole report, close the report and go to the AOT again. Go to body section for CustTable and set the property GrandTotal to Yes. The body section CustTrans does not have this property, only CustTable have the property as it is the primary data source. In step 2 the control to be summed was defined. In the design it was specified that AmountMST must be summed. This is in fact the only settings needed to be done. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 16 of 40 The user will at runtime be able to do the rest. The sorting field for breaking the sub total and the settings for the grand total will just be the default settings for the report. The AutoDesignSpecs node has a property called GrandTotal. This property will print a total for the report with the default label 'Super Grand Total' if set to Yes. Super Grand Total and the grand total set from either the report dialog or the body section will always give the same result. Both will print a total for the whole report. So if the user has the option of setting the grand total from the report dialog, you should not use the super grand total. As you might have noticed there is a property called auto header. This is used in the same way as auto sum. Instead of printing totals, a header will be printed each time a sorting field breaks. The user can control auto header at runtime, but if needed you can default auto headers to be visible. Both auto sums and auto headers are features only available in auto designs. All sections mentioned so far are triggered by either the reports framework or the reports query. You will have situations where you need to trigger a report section manually. Programmable sections are the answer for this, and as you might have guessed programmable sections are executed from X++. Example 5: Programmable section Elements used from MorphxIt_Report project � Report, MyReport_ProgSec � Menu item output, MyReport_ProgSec A programmable section is added to MyReport. For the simplicity the section will just print a text control. 1. Start duplicating MyReport, and rename the new report MyReport_ProgSec. Go to the report design node AutoDesignSpecs, right-click the node and choose new ProgrammableSection. 2. Open the property sheet for the new programmable section and locate the property ControlNumber. The control number is used to reference the section from X++. Set the ControlNumber to 10. 3. Now add a control to the programmable section. Right click the programmable section and choose New Control to add a text control. Go to the new text control, located the property Text and enter 'Header for customers'. 4. Now define the execution of the programmable section. Go to MyReport_ProgSec/Methods, right-click and choose Override Method and select init(). The init() method must look like: Public void init() { super(); element.execute(10); MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 17 of 40 } 5. Create a menu item for the report MyReport_ProgSec. When executing MyReport_ProgSec the text 'Header for customers' will be printed before the query is traversed. In the example the number for the programmable section was changed to 10. It is recommended to have steps between your programmable sections. If you at a later time need to add a new programmable section you will still be able to give your programmable sections a logical numbering. The execution of a programmable section can be called from X++ where needed. However when used in combination with auto sums some reservations must be taken. Say you want to print a programmable section before a body section is printed, then the logical place to add your code will be in the executeSection() method just before super() in the body section. This will print your programmable section before the body section is printed alright, but the programmable section will also be executed before each auto sum. At runtime Morphx treats an auto sums as a footer, and this causes the body section to be executed again. The solution is to use the report methods header() or footer() instead. From here you can control which body section is being executed. When a body section is executed, the parameters _tableId and _fieldId will contain a value. public void header(ReportSection _headerSection, tableId _tableId, fieldId _fieldId) { if (_tableId == tableNum(custTable) && _fieldId) element.execute(10); super(_headerSection, _tableId, _fieldId); } Here the header() method is used. A check is made to secure that it is the body section for the customer table which is printed. If so the programmable section is executed. Another way of using the header() and footer() method could be to add a new page after a sum if you are printing a batch of vendor or customer transactions. Note: Programmable sections are often used to insert separators like row spaces or lines. Doing so can be done by setting the properties of the programmable section. You will need to add a "dummy" control to your programmable section as if the programmable section does not have any controls the section will not be printed. Generated design Using generated designs can at first glance seem easier to use than auto designs, as you have more sections to construct your report from and all sections are visible in the report. However by using the generated designs your report will be more static as you generate the design based on the settings from the query and property settings from the design node. The disadvantages are that if you have chosen a report template for your report and later on decide to change the report template or simply choose another template, it will not reflect your design. Secondly the user will not be able to choose summarization when executing the report. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 18 of 40 To have a closer look at a generated design MyReport will be used to create a generated design. Go to MyReport and localize the node Designs/AutoDesign1. Right click the node and choose Generate Design. A new node called Generated Design has now been created below Designs/AutoDesign1. If you unfold the generated design you will notice that a design similar to the auto design is created. The difference is that the sections from the report template and the sections form calculating totals have been added. If you are missing the overview of you report when using auto designs, the option to create a generated design based on your auto design can come in handy, as all sections based on your templates and auto sums will be populated. Controls in design The most common way to add controls to your design is by dragging fields or display methods from a data source or direct from a table. When dragging a field or display method Morphx will create a control of the same type. This goes for basic types like string, enum, integer, real date and time. The controls like prompt, shape, sum and field group are used for more special purposes and must be added manually. You can of course add all type of controls manually, but it speeds up just dragging the controls as Morphx will auto position the control and fill out the properties with reference to either the field or the display method. Auto designs and generated designs have the same controls. For an overview of the available controls see figure 7, report controls. Name Description String Used for string values. If printing memo fields, the property DynamicHeight auto adjust the height of the control according to the number of lines printed. Enum Used to print the value of base enums. Integer Used for integer values. Real Used for real values. Date Used for printing dates. Dates will be formatted accordingly to the Windows regional settings. Time Used for printing the time. Time will be formatted accordingly to the Windows regional settings. Text Used for printing fixed text values. If the text must contain a dynamic value, display methods returning a string is normally used. Prompt Prompt will add text with following dots and a colon to the text. Shape Will draw a box. Size and position is specified by the properties. Can be used to build the layout for a formula. Bitmap Used for printing graphic. Enter path to the bitmap, refer to a container or use resources. For an example of using a MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 19 of 40 resource as a bitmap, see the report HRMApplicantStatus. Sum Used for printing sums with generated designs. With auto designs sums can be used for printing a sum in a programmable section. For an example of the use in auto design, see the report SalesLinesExtended. Field group Used for adding a field group from the data dictionary. The field group on the report will automatically be updated if changes are made to the field group in the data dictionary. Figure 7, report controls Bitmap can be at bit tricky to use as different options exist. You can use icons from Axapta resources. Either you key-in the resource id in the property sheet or you can make a display method returning the resource id. To get an overview of the available resource use the report tutorial_Resources, the report prints the resource id and the corresponding icon. Other options are to enter a path to a bitmap or refer to a bitmap stored in a container. When referring to a path remember using double backslash. The sum control is in generated designs used in the footer section. When used in auto designs the sum control must be put in a programmable section. Note: When printing a report the infolog may say that the report is scaled to fit the page. This is caused by too many columns in your design. Set the property FitToPage to No on the design node to disable scaling. When adding controls to your design you must assure that controls referring to a data source are fetched at the time the section is printed. That means, if looking at the MyReport example, you cannot print a control from the body sections CustTable and CustTrans in a page header section, because when the page header is sent the body sections are not fetched yet. The same goes for adding a control to the body section CustTable with reference to a field from CustTrans. This will also give an error as CustTable is fetched before CustTrans. 1.3 Methods on a report Creating simple reports like printing a list of inventory items, your needs can normally be fulfilled without having to write X++ code and simple using the facilities the report generator provide. For more advanced needs like filtering data based on a caller or a specific sorting you will need to override the methods on the report. For an overview of the methods on a report see figure 8, report methods. The query methods are described in the query chapter, see Queries. The report system classes are often used when modifying reports at runtime. They are the fundamental parts of reports and they give the opportunity to have a handle on each element of a report. In fact, you can create a report from scratch using the system classes. For more information on the system classes see the chapter Classes. For a complete reference on report system methods, see the appendix Reports appendix. Name Parameters Description MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 20 of 40 CallMenuFunction MenuFunction _menuFunction WebReport property Caption str _reportSpelling, str _reportName, str _designCaption, str _designName Used for setting the caption for the report. The parameters _reportSpelling and _designCaption set the captions in the print to screen window. CreateProgressForm Overloads the standard progress form executed when the report pages are created. The method gives the option to create your own progress form. Dialog Object _dialog Dialog() is used when adding fields to the reports dialog. The report runbase framework will call the dialog when the report is executed. See report KMAction. Fetch This method is the engine of the report. Fetch() is opening the user dialog, selecting the records from the database by processing the query and sending the records to be printed. This method is overloaded, when an expression cannot be specified in a query. An example could be printing detail information, see report HRMCourseSkills. Footer ReportSection _footerSection, tableId _tableId, fielded _fieldId The method is triggered each time a section in the design is executed. As auto sums is not a part of the design, this gives the option to execute code before or after auto sums is printed. GetTarget Returns the selected print medium. Header ReportSection _headerSection, tableId _tableId, fieldId _fieldId The method is triggered each time a section in the design is executed. As auto sums is not a part of the design, this gives the option to execute code before or after auto sums is printed. Init This is the first method called. The method is initializing the report. Entities used in the report are typically initialized here. See report salesFreightSlip. New anytype _argsOrReportOrContainer, str _designName, boolean _isWebReport=FALSE Used to initialize a reportRun object. This is normally not done from the report generator. The common case would be if a report is initialized from X++. Pack This method is used for storing last values. It is used in conjunction with unpack(), which is loading the last value stored. However unpack() is not MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 21 of 40 a base method. If a new dialog field has been added, pack() is overloaded to store the values from the dialog. See report KMAction. PageFormatting The method is not used anymore. From version 3.0 the method PrintJobSetttings.PageFormatting() is used instead. Print Print() is returning the number of pages to be printed. The method can be used to check whether there are any pages to print. See report projTimeSheetEmpl. PrinterSettings int _showWhat=-1 Used to activate the different parts of the printer dialog. The method is only called if the report runbase framework is not in use, as it is called from the prompt() method. ProgressInfo int _pageNo, int _lineNo ProgressInfo is executed for each line printed on the form. Prompt boolean _enableCopy=TRUE, boolean _enablePages=TRUE, boolean _enableDevice=TRUE, boolean _enableProperties=TRUE, boolean _enablePrintTo=TRUE Prior to version 3.0 prompt() was handling report dialog. Now the dialog() method is used instead. The method cannot be used in combination with the report runbase framework as the framework will overrule the settings. The class PrintJobSettings must be used instead. Run Run() is called when the Ok button is pressed in the dialog. Run() does the following steps. • If no generated design exists, a design is created on the fly based on the auto design. • Calling fetch() • Calling print() The method can be used for adding ranges to the query after the based on settings in the dialog. See report ReqPO. Send Send() is related to fetch(). Fetch() is iterating through the query records, and send() is sending the records to the design. The method can be overloaded to validate whether the record should be printed. See report CustTransList. SetTarget PrintMedium _target Set the target media for the report MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 22 of 40 ShowMenuFunction MenuFunction _menuFunction WebReport property ShowMenuReference WebMenu _menuReference WebReport property Title str _title="" Not of much use anymore. Can override the caption bar when printed to screen, if executed from fetch(). ExecuteSection Each section in the design has the executeSection method, which is used to print the section. The method can be used to validate whether the section must be printed. See report CustCollectionJour. Figure 8, report methods The previous parts of this chapter have focused on the single elements reports consist of. Now it is time to dig into how to use X++ for modifying your reports. Report runbase framework You might have wondered why you sometimes get different dialogs when executing your report. If a report is executed from the AOT you will first get the query dialog and second the printer dialog. When a report is executed from a menu item you will get only one dialog. By version 3.0 of Axapta a new runbase class runbaseReportStd was introduced. If a report is called from a menu item RunbaseReportStd will be called from the class SysReportRun. This means that the class runbaseReportStd will be called by the report runbase framework if your report is not called from a class inherited from the runBaseReport class. However when a report is executed directly from the Reports node the runbase report framework is not executed, and the two dialogs are shown. Note: reports are often used to follow up on data in the system. Here it might be neat having the report updating data. In Axapta it is best practice not to have reports writing to the database. If your report must update or insert records, you should consider creating a class doing the data manipulation and have the class called from a class inherited from the report runbase framework. It is good practice always to create a menu item to execute your report as you will then have the same dialog shown as the users. Keep in mind that a report executed from a class always must be inherited from the class runBaseReport. The class RunbaseReportStd is only to be used by the framework. For more information on the runbase classes, see the chapter Classes. Figure 9, Runbase report classes In prior versions of Axapta it was a common rule when creating a report that the report had to be called from an inherited runBaseReport class. This was done to wrap the two above mentioned report dialogs, making the report batch able and for better MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 23 of 40 performance as the class can be set to run on the server. With the introduction of the runBaseReportStd class only reports with heavy database load should be inherited from the runBaseReport class. Example 6: Report runbase Elements used from MorphxIt_Report project � Class, SalesReport_DailyEntries � Report, SalesDailyEntries � Menu item output, SalesReport_DailyEntries To get hands on the report runbase framework go to the Classes node and locate the class SalesReport_DailyEntries. The class is inherited from the runBaseReport class. This is a common report class named with a prefix for the module. You will see a lot of similar report classes when traversing the application classes. SalesReport_DailyEntries calls the report DailyEntries. Actually this class is of no need in v3.0 of Axapta, as the logic of the class is handled by the runBaseReportStd class. The class SalesReport_DailyEntries is a good example of how to construct a report class so it is used to simplify the case. The class has the following methods: public identifiername lastValueElementName() { return reportStr(SalesDailyEntries); } Here the name of the report is specified. The method must be overloaded, as the method specifies the name of the report. The function reportStr() secures that the report name entered is a valid report name. client server public static ClassDescription description() { return "@SYS77491"; } This method is defining the caption name for the report dialog. This method is optional. static void main(Args args) { SalesReport_DailyEntries salesReport_DailyEntries;

salesReport_DailyEntries = new salesReport_DailyEntries(); if (salesReport_DailyEntries.prompt()) { salesReport_DailyEntries.run(); } } The main() method is a static method which initializes the class. This makes the class run able so the class can be executed from a menu item. A check is made to validate whether the dialog is called. If Ok is pressed in the dialog, the report is executed. For using the class in the report the following have been added: MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 24 of 40 public class ReportRun extends ObjectRun { SalesReport_DailyEntries salesReport_DailyEntries; } In classDeclaration a variable for the class salesReport_DailyEntries is declared. public void init() { super(); salesReport_DailyEntries = element.args().caller(); if (!salesReport_DailyEntries) { throw error(Error::missingRecord(funcName())); } } An instance of the class SalesReport_DailyEntries is created. A check is made to validate whether the report is called from the classs salesReport_DailyEntries. This is done to prevent the report being executed directly from X++ or the AOT, so the logic from the class would not be called. In this example it would not matter from where the report was called. But it could be the case that the class was filtering data to be printed. As mentioned earlier the class SalesReport_DailyEntries is not needed as the runBaseReportStd class will handle the logic. Try changing the report not to use the class. The only change needed is to modify the init() method of the report. As init() is only initializing and doing validation for the class you can simple delete init(). The menu item is still referring to the class, so you will have to go to the output menu item SalesReport_DailyEntries and change the properties for the menu item so it calls the report instead. When executing the report you will get the same result as if you were using the class SalesReport_DailyEntries. Example 7: Report dialog Elements used from MorphxIt_Report project � Report, SalesDailyEntries � Menu item output, SalesDailyEntries_Without_Class Now it is time to add some more features to the SalesDailyEntries report. A dialog field for specifying whether details must be printed will be added. The value of the new field will be stored, so the last value is loaded when executing the report. The following must be added: public class ReportRun extends ObjectRun { DialogField dialogPrintDetails; Boolean printDetails;

  1. DEFINE.CurrentVersion(1)
  2. LOCALMACRO.CurrentList

printDetails

  1. ENDMACRO

MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 25 of 40 } DialogPrintDetails is a variable of the class DialogField and is used for the new dialog field. The variable printDetails stores the value of the dialog field. The macro CurrentList is a list of variables to be stored. The list will typically contain a variable for each field in the dialog. In this example CurrentList only contains a single variable, but it could be as many as needed, simply separate the variables by a comma to add more. CurrentVersion is keeping track of the stored version of CurrentList. If changes are made to CurrentList, then CurrentVersion must be incremented by one. public Object dialog(DialogRunbase _dialog = null) { DialogRunBase dialog;

dialog = super(_dialog); dialogPrintDetails = dialog.addFieldValue(typeId(NoYesId), printDetails, "Print details", "Print additional information for the transactions."); return dialog; } This method defines the dialog. The dialog is initiated from super() and then dialog will contain default dialog for the report. The only thing needed is to add the new field for printing details. The new field will automatically be put in a default field group called Parameters. public boolean getFromDialog() { boolean ret; printDetails = dialogPrintDetails.value(); ret = true; return ret; } When Ok is pressed in the dialog this method is called. The value from the new dialog field is stored in the printDetails variable. public container pack() { return [#CurrentVersion, #CurrentList]; } The last value from the new dialog field is stored. Pack() is saving the current value. CurrentVersion and CurrentList specified in ClassDesclaration are used as keys and field list. public boolean unpack(container packedClass) { boolean ret; Integer version = conPeek(packedClass,1); switch (version) { case #CurrentVersion: [version, #CurrentList] = packedClass; ret = true; break; MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 26 of 40 default: ret = false; } return ret; } The method is loading the last value stored using CurrentVersion and CurrentList. public void run() { if (printDetails) { SalesLine_Name.visible(true); } else { SalesLine_Name.visible(false); } super(); } The last step is to do the check whether details must be printed or not. In this example the difference will be whether SalesLine.name is printed or not. SalesLine_Name is declared by setting the property AutoDeclaration for the control SalesLine_Name to Yes. When a control is auto declared you will have access to change the properties for the control at runtime. I the report dialog example the methods pack() and unpack() were used. These are the methods used to store the last value of a dialog. If you need this functionality for your dialog, you can just copy the two methods from an existing class, and add CurrentVersion and CurrentList to the ClassDeclaration. Dynamic reports The ability of doing changes at runtime is very useful, as it gives the option of changing properties or adding new elements at runtime. This gives the option of creating one report, instead of having several reports with similar designs. The report SalesInvoice is an example of this, depending on the sales parameter settings SalesInvoice is printed with different controls visible. As Axapta is a multi language system, the use for printing reports in several languages is a must. Axapta will handle this alright. The report will by default be printed in the language Axapta is started up with. However if you print a formula like a sales invoice, the report must be printed in a customers preferred language. This is done by setting the property Language on the node ReportDesign. You can set the property to a fixed value, but the right way will be setting the language from X++. Here myTable.languageId is used to set the language for the design: public void init() { super(); element.design().laguageId(myTable.languageId); } MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 27 of 40 The keyword element is used within the report to reference all of the reports objects. As here the element is referring to the method languageId() in the object design. In this way you can get a handle on each element of the report. Using element to reference report object might result in a long path, consider this: element.design().sectionGroup(tablenum(CustInterestTrans)).section(ReportBlockType::BODY).contr olName('custInterestTrans_custInterestJourInterestAmount'); The above line is from the method init() in the report CustInterestNote. A better way is to use the property AutoDeclaration. When using the AutoDeclaration property you are actually declaring an instance of a system classes. SalesLine_Name used in the report dialog example is an instance of the system class ReportStringControl. Note: When browsing the standard reports in the AOT, you may see reports where system classes for sections and controls have been declared manually from code, rather than using the AutoDeclaration property. First by v3.0 of Axapta the AutoDeclaration property was added to report controls. The system classes give the opportunity creating reports from scratch though it is not the intention. The system classes are meant to be used when you need to add elements to a report at runtime, like sections and controls. The case could be that you at runtime would print detailed information or print an extra section from a different table depending on where your report is called from. Instead of using the system classes creating the controls at runtime, you can just add all the sections and controls needed for the different combinations and then use the property visible. In some cases using the system classes would be preferable. By using system classes for creating controls, you can wait till runtime deciding the type of your control. This is neat if the controls you need to add depend on the user settings in a form. Example 8: Report system classes Elements used from MorphxIt_Report project � Report, MyReport_SystemClasses � Menu item output, MyReport_SystemClasses To try out the report system classes, create a new report as shown in figure 10, test of report system classes. The report will at runtime create a body section for CustTable. The body section will have 10 controls, printing the value of the first 10 fields from CustTable. The table CustTable is added a as data source and the node for the auto design has been created. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 28 of 40 Figure 10, test of report system classes Now go and overload the init() method of the report. Init() will hold all the code for the report. public void init() { ReportSection reportSection; DictTable dictTable; Counter fieldCounter; super(); reportSection = element.design().autoDesignSpecs().addSection(ReportBlockType::Body); reportSection.table(tableNum(custTable)); dictTable = new DictTable(tableNum(custTable)); while (fieldCounter < 10) { fieldCounter++; dictField = new DictField(dictTable.id(), dictTable.fieldCnt2Id(fieldCounter)); reportSection.addControl(dictTable.id(), dictTable.fieldCnt2Id(fieldCounter)); } } The instance of the system classes reportSection and reportControl is used to create the body section and the controls for the body section. A new report section of the type body section is added to the node AutoDesignSpecs specified to use the table CustTable. DictTable are also an instance of a system class. DictTable is often used when a handle is needed for table and field properties. Here dictTable is used to loop the first 10 fields in CustTable. For each loop a control is added to the body section. That is all. Morphx will handle adding the proper control type, and auto adjusting your controls, so when you run the report you will have the first 10 fields of the table CustTable printed in a row. If you are creating a module where the user must have the option of defining their own layout of a report, using the system classes could be the answer. Common report methods When doing modifications to a report you are overloading existing methods, or adding new methods which are called from an overloaded method. The following methods are executed in listed order when a report is loaded: MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 29 of 40 init() � dialog() � run() � fetch() � send() � print() Init() and dialog() are triggered when the report is loaded. Run() is triggered when the button Ok is pressed in the dialog. Fetch() is looping the query and for each record found send() is triggered. At last print() is triggered. These methods are the most important methods of a report, and often it will be the methods you need to override. Common situations are when adding controls to a dialog or need to control the output from the query before it is printed like if a programmable section must be printed with a body section. The above listed execution order is when the report runbase framework is in use. If you call your report directly without using a menu item, the execution order of the methods is slightly different: init() � run() � prompt() � fetch() � send() � print() Dialog() will not be triggered. RunBaseReportStd is controlling the dialogs of the report, and when the runbase framework is not in use prompt() is used. Example 9: Overloading fetch() Elements used from MorphxIt_Report project � Report, MyReport_Fetch � Menu item output, MyReport_Fetch Now try doing an overload of fetch(). A new report will be created printing customer transactions for each customer, filtered from n-days till system date. The report will have a dialog where the n-days is keyed in by the user. Start by duplicating the MyReport example. The report will end up as shown in figure 11, report for overloading fetch(). The example will focus on overloading the methods. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 30 of 40 Figure 11, report for overloading fetch() As you now have created the query and the design for the report, you are ready to create the methods for the report. public class ReportRun extends ObjectRun { DialogField dialogDaysBack; NumberOf daysBack; } The variable dialogDaysBack is needed for the dialog. The value will be stored in the variable daysBack. public Object dialog(Object _dialog) { DialogRunBase dialog;

dialog = super(_dialog); dialogDaysBack = dialog.addFieldValue(typeId(NumberOf), daysBack, "Number of days", "Number of days back to be printed."); return dialog; } No news here. A field is added to the dialog to enter the n-1 days. public boolean getFromDialog() { boolean ret; MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 31 of 40 daysBack = dialogDaysBack.value(); ret = true; return ret; } The variable daysBack is set to store the value from the dialog. public boolean fetch() { QueryRun qr; QueryBuildRange rangeTransDate; Boolean ret; qr = new QueryRun(element); rangeTransDate = element.query().dataSourceTable(tablenum(CustTrans)).addRange(fieldnum(CustTrans, transDate)); rangeTransDate.value(queryRange(systemdateGet()-daysBack, systemDateGet())); rangeTransDate.status(RangeStatus::LOCKED); element.design().caption(strFmt("%1, %2", element.design().caption(), rangeTransDate.value())); if (qr.prompt() && element.prompt()) { while (qr.next()) { custTable = qr.get(tableNum(CustTable)); custTrans = qr.get(tableNum(CustTrans)); if (!custTable) { ret = false; break; } if (qr.changed(tableNum(custTable))) element.send(custTable, 1); if (qr.changed(tableNum(custTrans))) element.send(custTrans, 2); } ret = true; } else ret = false; return ret; } Here goes the essential part. The variable daysBack contain the value the user has keyed in. A range must be added to the query to filter the transaction date with n-1 days to system date. A QueryRun object called qr is initialized with the active query of the report, and then a range for the customer transaction date is added to qr. The range is locked, so the user cannot change it. The transaction date range is added to the caption of the report. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 32 of 40 Then the query is looped. The loop of the query and the print of the record is what the super() call in fetch() handles. Before the query is looped there is a check to see whether the dialogs for the query and the report is called, the two dialogs which runBaseReportStd wraps. For each loop the tables CustTable and CustTrans are initialized. If no records are found, the loop breaks and the report end. If a data source has changed a new record is found, and the record is printed using the send() method. Note the second parameter in the send method. The second parameter is defining the level of the record. CustTable is on the first level of the query, and CustTrans is on the second level. This is important, as if it is not set correctly, auto sums will not be printed. In the fetch example the query of the report was looped. The case could also be looping a while select statement or a combination of both. For each record looped in the query, you might want to select a record from a table which is not part of the query or even build a temporary table to be printed. All you have to do is for each record to be printed, the send() method must be called. If your reason for overloading fetch() is doing validation of which records to be printed it will be easier overloading the send() method instead: public boolean send(Common _cursor, int _level=1, boolean _triggerOffBody=TRUE, boolean _newPageBeforeBody=FALSE) { boolean ret; CustTrans custTrans; if (_cursor.tableId == custTrans.tableId) custTrans = _cursor; if (custTrans.transDate == systemDateGet()) ret = super(_cursor, _level, _triggerOffBody, _newPageBeforeBody); return ret; } The send() method has the record to be printed as cursor. All you need to do is initializing your table. Here the table CustTrans is initialized if the cursor is a CustTrans record. Only customer transactions with transaction date equal to the system date will be printed. Adding query values can often be handled just by overloading the init() method, which is much easier as only a few lines of code is needed. The range added to the query in the fetch example was dependent on user interaction. The modification had to be done after the dialog was closed, so the code had to be put in fetch(). Before adding a range to a query, you should always do a check for whether the query already contains a range for the field, by using the QueryBuildRange.findRange() method. If two ranges are created for the same field, the ranges will be and'ed which may give you unexpected results. The user has an option in the report dialog to print ranges for a query, when printing to a printer. Both ranges added to the query data sources and the ones added from X++ will be printed, however if fetch() is overloaded this option will be disabled. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 33 of 40 Special reports Till now this chapter has been focused on the basic steps in creating reports. To get an idea of the options within the Morphx languages this section will show examples on how to solve different cases on reports with special needs and how you can make your reports more users friendly. Execute report from X++ Reports are normally activated from a menu item, called from either the main menu or from a form. Sometimes it can be necessary to call the report directly from X++ as the user for some reason may not call the report directly from a menu item. Elements used from MorphxIt_Report project � Job, ExecuteReport � Job, ExecuteReportSilent static void ExecuteReport(Args _args) { Args args; SysReportRun reportRun;

args = new Args(); reportRun = new menuFunction(menuItemOutputStr(MyReport), MenuItemType::Output).create(args); reportRun.run(); } The job shows how MyReport is called from X++. Notice the application class SysReportRun is used. SysReportRun is inherited from the system class ReportRun. The benefit of using the application class is that you have the option to override the code in the SysReportRun class. You could also create your own extended class inherited from the SysReportRun class. This could be the case if you have a series of reports where checks must be made when printing, rather than modifying each single report. MyReport is called using the menu item as this will trigger the runbase report framework and having the correct dialog for the report loaded. If you do not want to use the runbase framework, or you want to print the report without user interaction you must instead trigger the report without using the runbase report framework. static void ExecuteReportSilent(Args _args) { Args args; SysReportRun reportRun;

args = new Args(); args.name(reportStr(MyReport)); reportRun = new SysReportRun(args); reportRun.query().interactive(false); reportRun.report().interactive(false); MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 34 of 40 reportRun.setTarget(PrintMedium::Printer); reportRun.run(); } In this job the report is executed without using the menu item and then the runbase report framework is not used. The example prints the report direct to the default printer without user interaction as both the query and the report dialog are set to inactive. If dialog() is overridden in your report, you must assure that this do not give any problems, as dialog() will not be executed. If your report consists of more than one design you must specify which design to be used. If not specified or if an invalid design name is entered, the first design for the report will be used reportRun.design("MyDesign"); reportRun.run(); Using temporary tables If a special sorting is needed, or you must select data from several tables which cannot be joined, using a temporary table can be the answer. Using temporary tables is in fact pretty simple. The temporary table must just be filled and passed on to the report. There will be a performance issue when using temporary tables, as the report will have two runs. First the temporary table is build, and second the temporary table is looped in the report. The use of temporary tables should not be your first choice. You might be better off reconsidering your design. For more on temporary tables see the chapter Data Dictionary. Elements used from MorphxIt_Report project � Classs, Reports_TempTable � Report, Reports_TempTable When using temporary tables you should have the report called from a class. This will give you the option building the temporary table at sever side. The following example shows how to create a report using a temporary table. The example is for simplicity just adding 10 records to a temporary table and printing the result. class Reports_TempTable extends runBaseReport { } The class is inherited from runBaseReport. public identifiername lastValueElementName() { return reportStr(Reports_TempTable); } The name for the report is specified. tmpAccountSum tempTable() { CustTrans custTrans; TmpAccountSum tmpAccountSum; Counter counter;

MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 35 of 40 while select custTrans { counter++; if (counter == 10) break; tmpAccountSum.accountNum = custTrans.accountNum; tmpAccountSum.currencyCode = custTrans.currencyCode; tmpAccountSum.balance01 = custTrans.amountMST; tmpAccountSum.insert(); } return tmpAccountSum; } The temporary table tmpAccountSum is used. The first 10 records from the table CustTrans are inserted in tmpAccountSum. This method is used by the report to pass on the buffer for the temporary table to the report. static void main(Args args) { Reports_TempTable reports_TempTable = new reports_TempTable(); if (reports_TempTable.prompt()) reports_TempTable.run(); } The class is initialized and the report is executed. Figure 12, Report using temporary table Last step is to create the report. A report with the temporary table TmpAccountSum as data source must be created. The 3 fields filled out with values from CustTrans will be printed. Your report must look like figure 12, report using temporary table. public void init() { Reports_TempTable reports_tempTable;

MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 36 of 40 super(); reports_TempTable = element.args().caller(); if (!reports_TempTable) throw error(Error::missingRecord(funcName())); reports_TempTable.queryRun().setRecord(reports_TempTable.tempTable()); } Init() must be overloaded. The runbase class is initialized and the query is set with a buffer to the temporary table. Notice you will just have to set a reference to the buffer. The query will then have the full scope of the temporary table, and loop all of the records in the temporary table. Coloring rows Colors are rare in the reports in the standard packing. The reason might be that you will have to fiddle a bit with the report to have a proper result. However the use of colors can give your report the final touch and even ease up reading the printout. Elements used from MorphxIt_Report project � Report, MyReport_Color � Menu item output, MyReport_Color This example will show how to color a single column based on a condition. The report will set the background color the control printing CustTrans.amountMST. To simplify the condition check is done from X++. In real life the conditions could be set up in a dialog or based on data in a form. You should end up with a design as shown in figure 13, report coloring rows. Figure 13, report coloring rows MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 37 of 40 1. Start out by duplicating the report MyReport, and rename it to MyReport_Color. 2. The goal is to color the control CustTrans_AmountMST. By using the MyReport example as it is the header label will also change color. Instead of the standard header for the body section CustTrans_Body, a new one must be created. To skip the standard header set the property NoOfHeadingLines to 0 on the body section CustTrans_Body. 3. Create a new header by adding a programmable section and add a prompt control for each of the 3 fields in the body section. The property ModelFieldName on each prompt control must be set to the corresponding body section control name. Add a label for each prompt control. 4. Declare a variable to keep track of when the programmable section used for header must be printed. public class ReportRun extends ObjectRun { Boolean printCustTransHeader; } 5. Now overload send(). If the current record is a CustTrans record the header is printed for the first CustTrans in a row. A condition is set up for CustTrans.AmountMST. If an amount larger than 500 is printed, the background color is set to yellow. Otherwise the background color will be neutral. public boolean send(Common _cursor, int _level=1, boolean _triggerOffBody=TRUE, boolean _newPageBeforeBody=FALSE) { boolean ret;

if (_cursor.tableId == tableNum(custTable)) printCustTransHeader = true; if (_cursor.tableId == tableNum(custTrans)) { if (printCustTransHeader) { element.execute(10); printCustTransHeader = false; } if (custTrans.amountMST > 500) CustTrans_AmountMST.backgroundColor(Winapi::RGB2int(255, 255, 0)); else CustTrans_AmountMST.backgroundColor(Winapi::RGB2int(255, 255, 255)); } ret = super(_cursor, _level, _triggerOffBody, _newPageBeforeBody); return ret; } MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 38 of 40 Print using Word Creating reports in Axapta with a complex design such as a formula with boxes, tables and graphic can be a battle. You sure can do it but, but it might be an idea trying out other tools. By using the com interface you can connect to external applications like Word. To use Word for printing data you must create a Word template with bookmarks. The bookmarks are used to position the data from Axapta. Notice, that you must have a license code for at least one com client to use the com interface. Note: The Document handling in the standard package uses the com interface to attach files from Excel and Word. The classes used by Document handling are prefixed with DocuActionCOM. Elements used from MorphxIt_Report project � Class, PrintUsingWord Additional � Word template, Reports_WordTemplate.dot This example will show how to connect to Word and create a new document printing data from the table InventTable. The first 10 records from InventTable will be printed. Labels will be printed for header and column headers. You will have to start creating a Word template, with the following bookmarks: label_header, label_itemid, label_itemname, and label_itemdesc. Label_header will print a heading text for the column. Create a table and add the remaining 3 bookmarks as header for the table. The next step is to create the following class: void run() { COM COMAppl, COMDocuments, COMDocument;

COMAppl = new COM('Word.Application'); COMDocuments = COMAppl.documents(); // enter path to the template Reports_wordtemplate.dot COMDocument = COMdocuments.add('d:\\Reports_WordTemplate.dot'); if (COMDocument) { this.setLabels(COMDocument); this.sendInventTable(COMDocument); this.showDocument(COMAppl); } } Run() will initiate the COM connection, and open a new Word document based on the template Reports_WordTemplate.dot. Remember to check that the path for the template is valid. If the document is created, labels and data will be added. Finally the document will be shown. Note that the document shown is not saved. If you want to save the document you must add: COMdocument.saveAs(<filename>,0,false,,false);. void setLabels(COM _COMDocument) { COM COMBookmarks, COMBookmark, COMrange; DictField dictField; MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 39 of 40 Label label;

COMBookmarks = _COMDocument.bookmarks(); if (COMbookmarks.exists('label_header')) { COMbookmark = COMbookmarks.item('label_header'); COMrange = COMbookmark.range(); COMRange.InsertAfter("Inventory list"); } if (COMbookmarks.exists('label_itemId')) { COMbookmark = COMbookmarks.item('label_itemId'); COMrange = COMbookmark.range(); DictField = new dictField(tableNum(inventTable), fieldNum(inventTable, itemId)); COMRange.InsertAfter(dictField.label()); } if (COMbookmarks.exists('label_itemName')) { COMbookmark = COMbookmarks.item('label_itemName'); COMrange = COMbookmark.range(); DictField = new dictField(tableNum(inventTable), fieldNum(inventTable, itemName)); COMRange.InsertAfter(dictField.label()); } if (COMbookmarks.exists('label_itemDesc')) { COMbookmark = COMbookmarks.item('label_itemDesc'); COMrange = COMbookmark.range(); label = new Label(CompanyInfo::languageId()); COMRange.InsertAfter(label.extractString(literalstr("@SYS58702"))); } } A check is made to see whether the bookmark can be found. If found the labels are set. The header label is set with the static text 'Inventory list'. The labels for the bookmarks label_itemId and label_itemName are set with the label for the corresponding table fields. The label for the bookmark label_itemDesc is set using the method label.extractString() to fetch the label for the default company language. void sendInventTable(COM _COMDocument) { COM COMTable, COMRows, COMRow; COM COMCells, COMCell, COMRange; InventTable inventTable; Counter counter;

//init tabel COMTable = COMDocument.Tables(); COMTable = COMTable.Item(1); COMRows = COMTable.Rows(); while select inventTable { counter++; MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com MORPHX IT Reports © 2005 Steen Andreasen 40 of 40 if (counter == 10) break; // add new row COMRow = COMRows.Add(); COMCells = COMRow.Cells(); // item id COMCell = COMCells.Item(1); COMRange = COMCell.Range(); COMRange.InsertAfter(inventTable.itemId); // item name COMCell = COMCells.Item(2); COMRange = COMCell.Range(); COMRange.InsertAfter(inventTable.itemName); // item description COMCell = COMCells.Item(3); COMRange = COMCell.Range(); COMRange.InsertAfter(inventTable.itemDescription()); } } Here the inventTable is looped. The table in Word is first initiated. For each loop a new row is added to the table in Word. The fields itemId, itemName and the display method itemDescription() from InvenTable is added to the 3 rows in the Word table. Notice that no bookmarks are needed void showDocument(COM _COMAppl) { _COMAppl.visible(TRUE); } The created Word document is shown. static void main(Args _args) { PrintUsingWord printUsingWord = new PrintUsingWord();

PrintUsingWord.run(); } No news here. Just initiating and executing the class. 1.4 Summary This chapter introduced you to reports in Axapta. The chapter has covered the basic in creating reports. By now you should be familiar with the single elements of a report, such as data sources, designs, sections and controls in designs and the common methods on a report. You should have acquired knowledge on how to create reports using the report generator, and hopefully you have got an insight in the possibilities with reports in Axapta. MORPHX IT - Reports, pre-release 2005-09-30 -- www.steenandreasen.com

Pages in category "Report development"

The following 7 pages are in this category, out of 7 total.