Customizing Microsoft Dynamics AX 2009

  • 6/10/2009

Report Customization

Reports, like forms, can be customized to include and exclude information, and you can modify their design and layout. As with forms, the design and layout of a report depend on settings on the table and on the report itself. The best practice is, once again, to keep as much of the business logic as possible with the table methods or metadata. The X++ code in reports must deal with the functionality for the specific report. All other X++ code must generally be implemented on the table to be reused by other areas in the application. Here are some of the advantages to such an approach:

  • Customizations made to a report are isolated; customizations made to a table affect all reports using that table, resulting in a consistent user experience wherever the table is used.

  • Customization of a report copies the entire report to the current layer; customizations made to tables are more granular because customization of fields, field groups, and methods results in a copy of the specific element to the current layer only. This makes upgrading to service packs and new versions easier.

  • Methods in reports always execute on the tier where the report is generated; methods on tables can be targeted to execute on the server tier. Where a report is generated is controlled by the RunOn property on the menu item that starts the report. The property can be set to Client, Server, or Called From.

Creating Promotional Materials

The example in this section demonstrates how to customize the sales order invoice report named SalesInvoice (AOT\Reports\SalesInvoice). The invoice is customized to include promotions based on items listed on the invoice. The promotion appears below each item on the invoice associated with a promotion. Figure 5-14 shows an example of an invoice that displays a promotion for a water bottle.

Figure 5-14

Figure 5-14. Promotion on an invoice

Like the forms example, this example uses the document management feature in Dynamics AX. You use document handling to store the text and image in the database. The information is attached to the item table as two different types of document information, named PromoText and PromoImage, for storing the text and image. Figure 5-15 shows the PromoText and PromoImage document types.

Figure 5-15

Figure 5-15. PromoText and PromoImage document types

Figure 5-16 shows the text and image attached to an item named PB-Bike.

Figure 5-16

Figure 5-16. Text and image attached to an item

The X++ code used to display the promotion on the invoice looks up the item in the InventTable table and searches the document handling for documents of type PromoText and PromoImage to print on the invoice. If neither type is attached to the item, no promotion information prints.

Adding Promotional Materials to an Invoice Report

Before you customize the SalesInvoice report for this example, you must decide where in the design of the report to place the printing of the promotion. The printed information should be printed for each invoiced item, so you must place it under the CustInvoiceTrans section group because the CustInvoiceTrans table contains the invoiced items. The CustInvoiceTrans section group contains a reference body section that can print other pieces of reference information, such as from inventory dimensions or the packing slip lines posted when the invoiced item is shipped. The promotion resembles this kind of information in terms of when and how it is printed.

This example, therefore, creates a new section group within the reference body section below the existing three groups. The new section group must reference a table type so that it can be invoked when a record buffer of the same type is sent to the report by using the element.send method. The DocuRef table stores the promotion text, and the DocuValue table stores the promotion image with an association created in the DocuRef table.

Although the storage of the text and image results in the creation of DocuRef records, the choice of DocuRef as the reference table type for the new section group isn’t an optimal solution. First, the information is stored as two records in the DocuRef table, but the text and image should be printed side by side for this example. The element.send method should be called only once, parsing in only a single record buffer. Also, two other section groups already use DocuRef as the table type, so using this type might result in the other section groups getting invoked as well when the promotion prints. You could prevent this by introducing a variable to control which section group to invoke, but then you would have to customize even more of the report, making it harder to upgrade the report when a new version or service pack is installed.

Both of the DocuRef records are, however, related to the same InventTable record, so you can use this table as the type for the section group, and an InventTable record buffer is sent to the report to print the promotion text and image. Figure 5-17 shows the new section group, named InventTable, and its positioning within the report.

Figure 5-17

Figure 5-17. InventTable section group in the SalesInvoice report

Implementing Promotional Methods

When the promotion text and image print, an InventTable record buffer is sent to the report. For this reason, this example implements two methods to return the text and image by using an InventTable record buffer. The methods can be implemented directly in the report, but because the methods are not report specific—and therefore can be reused in other reports, or even forms—they are implemented as instance methods on InventTable. The following code shows the new methods. The PromotionImage method is implemented like the BikeImage method in the forms example discussed earlier. However, the PromotionImage method must look in only the DocuRef table to find the text.

display server public DocuValueFile PromotionImage()
{
    DocuRef     docuref;
    DocuValue   docuValue;
    ;
    select  firstonly tableid from docuRef
        where docuRef.RefCompanyId  == this.DataAreaId  &&
              docuRef.RefTableId    == this.TableId     &&
              docuRef.RefRecId      == this.RecId       &&
              docuRef.TypeId        == 'PromoImage'
    join file from docuValue
        where docuValue.RecId   == docuRef.ValueRecId;

    return docuValue.File;
}



display server public Notes PromotionText()
{
    DocuRef     docuref;
    ;
    select firstonly notes from docuRef
        where docuRef.RefCompanyId  == this.DataAreaId  &&
              docuRef.RefTableId    == this.TableId     &&
              docuRef.RefRecId      == this.RecId       &&
              docuRef.TypeId        == 'PromoText';

    return docuRef.Notes;
}

Both methods are implemented as display methods to allow them to bind directly to report controls and to print the information.

Binding Display Methods to Report Controls

The next step is to bind the methods to report controls. A new body section named BodyInventTable is created in the InventTable section group, and several of its properties are altered, as shown in Table 5-13.

Table 5-13. BodyInventTable Property Settings

Property

Settings

NoOfHeadingLines

0

LineAbove

Solid

LineBelow

Solid

LineLeft

Solid

LineRight

Solid

The NoOfHeadingLines property must be set to 0 because the text and image must not include any headings when printed. The Line property settings create a border around the promotion.

In the body section, a string control, named PromotionText, and a bitmap control, named PromotionImage, are added and bound to the two new InventTable methods. The properties shown in Table 5-14 are changed on the two controls.

Table 5-14. PromotionText and PromotionImage Property Settings

Property

PromotionText

PromotionImage

Left

Auto (right)

Width

70.00 char

2.0 inch

Height

2.0 inch

DynamicHeight

Yes

ShowLabel

No

No

Table

InventTable

InventTable

DataMethod

PromotionText

PromotionImage

The ShowLabel properties are set to No because no headings should be printed. The PromotionText control is set to a fixed width of 70 characters with a dynamic height so that the text won’t be truncated. The PromotionImage has a fixed size of 2 inches by 2 inches and is right-justified on the page.

The last step is to look up an InventTable record buffer based on the invoiced item and then send the buffer to the report. You do this with the following new method on the BodyReference body section.

void printInventTable()
{
    InventTable inventTable = custInvoiceTrans.inventTable();
    if (inventTable.RecId)
    {
         element.send(inventTable);
    }
}

The method uses the InventTable lookup method on the CustInvoiceTrans table, which returns a record buffer for the invoiced item, which the method subsequently sends to the report.

The preceding method should be called from the executionSection method on the same body section. The following method is therefore customized by including the call to the printInventTable method.

void  executeSection()
{;
    this.printCustPackingSlipTrans();
    this.printDimHistory();
    this.printInventTable();
}

The positioning of the body section, report control, and report methods is shown in Figure 5-18.

Figure 5-18

Figure 5-18. Position of the new sections, control, and methods in the SalesInvoice report

After the completion of all the customizations to the SalesInvoice report and the addition of new methods to InventTable, the report prints the promotion below each invoiced item on the report, as shown in Figure 5-14.

Preventing Printing of an Empty Body Section

The solution thus far has one flaw: it prints an empty BodyInventTable body section if there is no document reference for the PromoText and PromoImage document types, which causes an empty box to appear below each item on the invoice. You could easily fix this by altering the printInventTable method to include a check for text or images, as shown in the following change to the printInventTable method.

void printInventTable()
{
    InventTable inventTable = custInvoiceTrans.inventTable();
    if (inventTable.RecId &&
        (inventTable.PromotionText() || inventTable.PromotionImage()))
    {
         element.send(inventTable);
    }
}

This code ensures that the InventTable record buffer is sent to the report only if the PromotionText method or the PromotionImage method returns a value.

In terms of performance, this change isn’t optimal because methods could be executed twice if a promotion were added to the InventTable record. This could result in as many as five round-trips to the database for each printed invoiced item: two from the printInventTable method, two when printing the values, and one when the report runtime determines the height of the PromotionText control.

A better solution is to cache the returned values from the PromotionText and PromotionImage methods when they are called in the printInventTable method and then use the cached values instead of retrieving them from the database when printing the PromotionText and PromotionImage controls.

The cache variables must be added to the classDeclaration of the report, so the following lines are inserted there.

DocuValueFile           promotionImage;
 Notes                   promotionText;

The printInventTable method is modified to store the returned values from the PromotionText and PromotionImage methods on the InventTable record buffer in the newly created variables, as shown in the following copy of the method.

void printInventTable()
{
    InventTable inventTable = custInvoiceTrans.inventTable();
    ;
    promotionImage  = inventTable.PromotionImage();
    promotionText   = inventTable.PromotionText();

    if (inventTable.RecId &&
        (promotionText || promotionImage))
    {
         element.send(inventTable);
    }
}

In addition to these two new display methods, PromotionText and PromotionImage are created to return the values of the variables. The following code samples show these methods, implemented in the BodyInventTable body section.

display Notes PromotionText()
{
    return promotionText;
}
display DocuValueFile PromotionImage()
{
    return promotionImage;
}

With these two methods named similarly to the InventTable methods, you must remove only the value in the Table property on the PromotionImage and PromotionText report controls to enable the report to retrieve the value from the local report methods instead of the InventTable methods. You can even remove the display method modifiers from the two InventTable methods because they are no longer used as display methods.

When you print the report again, no empty BodyInventTable body sections appear, and the printing of this specific section is optimized. The report will never result in more than two round-trips to the database for each invoiced item. The only disadvantages are that return types of the methods on the InventTable and the equivalent methods on the report should be kept synchronized, and these return types should again be kept synchronized with the types of the cache variables. This synchronization wasn’t necessary earlier in the example, before the values in the report were cached.