Using Zen Reports
Building Zen Report Classes
[Home] [Back] [Next]
InterSystems: The power behind what matters   
Class Reference   
Search:    

The Zen Report Tutorial section of the chapter “Introducing Zen Reports” explains that a Zen report is a class that extends %ZEN.Report.reportPage. The Zen Report Tutorial explores the structure of a Zen report class by building it in gradual steps.

Other chapters explain how to write the XData ReportDescription and XData ReportDisplay blocks that cause the Zen report class to generate report output in XHTML and PDF formats. These chapters are Gathering Zen Report Data,” Formatting Zen Report Pages,” and Displaying Zen Report Data.”
Building on the foundation established by these prior chapters, this chapter explores Zen report class structure and organization in greater detail. Topics include:
Controlling Zen Reports with Parameters
The word parameter is widely used. This section describes several kinds of parameter that you can use to change the way a Zen report class operates. In each case, the item is called a parameter when it is used to control the Zen report, but in each case, the context and syntax for using the parameter are different.
Class Parameters
A class parameter is an ObjectScript convention that you can use in Zen report classes. For an overview, see Class Parameters in the “Caché Classes” chapter of Using Caché Objects.
The Zen Report Tutorial section of the chapter “Introducing Zen Reports” introduced the class parameters APPLICATION, DEFAULTMODE, and REPORTXMLNAMESPACE, which the Zen Report Wizard automatically provides when you create a new Zen report class in Studio. A Zen report class supports many additional class parameters. For details, see the following sections in the appendix Zen Report Class Parameters:
SQL Query Parameters
When you supply the SQL query that populates your Zen report with data, this query can include SQL parameters, which Zen reports support in the same way as Zen pages. The following is an example of an SQL query that accepts parameters. The ? character is the placeholder for parameters in the query:
SELECT ID,Customer,Num,SalesRep,SaleDate
    FROM ZENApp_Report.Invoice
    WHERE (Month(SaleDate) = ?) OR (? IS NULL)
    ORDER BY SalesRep,SaleDate
The following is an example of a Zen report that provides its data using the sql attribute with <parameter> elements to supply values to the ? placeholders:
Note:
Each ? placeholder in the query requires its own <parameter> element.
<report
  xmlns="http://www.intersystems.com/zen/report/definition"
  name="myReport"
  sql="SELECT ID,Customer,Num,SalesRep,SaleDate
    FROM ZENApp_Report.Invoice
    WHERE (Month(SaleDate) = ?) OR (? IS NULL)
    ORDER BY SalesRep,SaleDate"
    orderby="SalesRep,Customer" >
    <!-- Supply values to the ? query parameters here -->
    <parameter expression='..Month'/>
    <parameter expression='..Month'/>
  <!-- Other report contents appear here -->
</report>
For detailed information about SQL queries and their parameters, see the section Building the <report> or <group> Query in the chapter “Gathering Zen Report Data.”
Data Type Parameters
A data type parameter is an ObjectScript convention that you can use in Zen report classes. For an overview, see Parameters in the “Data Types” chapter of Using Caché Objects.
The most useful data type parameter for Zen reports is ZENURL. If a developer assigns the ZENURL parameter to a Zen report class property, this enables a user to set the value of that property at runtime by providing a query parameter in the URI when invoking the Zen report. The ZENURL value names the URI query parameter. Note that by convention, ZENURL values starting with dollar sign (“$”) are reserved for predefined URI query parameters such as $MODE. For example:
Property employeeID As %ZEN.Datatype.string(ZENURL="ID");
For details, see Setting Zen Report Class Properties from the URI in the chapter “Running Zen Reports.”
XSLT Stylesheet Parameters
It is possible for a user to pass XSLT stylesheet parameter values to Zen reports via URI query parameters when invoking the Zen report class. Parameters defined in this way become XSLT global variables inside the XData ReportDisplay <report> element. This convention requires careful coordination of an <xslt> element within the XData ReportDisplay block with a Zen report class property that has at least one property with the ZENURL data type parameter defined.
For details and a complete example, see <xslt> in the chapter “Formatting Zen Report Pages”
URI Query Parameters
In addition to the URI query parameters that you might introduce with ZENURL, Zen reports offer several predefined URI query parameters that you can use at runtime to override the values set by the corresponding Zen report class parameters. By convention, the names of these parameters begin with dollar sign (“$”).
The following example uses the $MODE parameter in the URI string to override any value that might have been set for the DEFAULTMODE parameter in the Zen report class. A value of $MODE=pdf changes the type of output to PDF:
http://localhost:57772/csp/myPath/myApp.myReport.cls?$MODE=pdf
For a summary of available URI parameters and their class parameter equivalents, see URI Query Parameters for Zen Reports in the chapter “Running Zen Reports.”
Using Runtime Expressions in Zen Reports
You can use runtime expressions in Zen report class XData ReportDisplay blocks. Simply use the #()# container to hold the expression. This convention allows you to reference the following items only:
The section Organizing Zen Reports to Reuse Code describes how to use composites and templates to define reusable portions of code that you can reference from XData ReportDisplay in the main Zen report class. Runtime expressions work in composite and template classes. This is because, at runtime, Zen integrates the XData material from composites and templates into the code generated by the XData ReportDisplay that references them. Any runtime expressions found in the composite or template classes become part of the higher level XData ReportDisplay block.
Note:
Runtime expression #()# syntax does not work in XData ReportDefinition blocks.
Many XData ReportDefinition and XData ReportDisplay elements support the use of expressions without the runtime expression #()# container. This is true of:
Localizing Zen Reports
The Zen Localization chapter of the book Developing Zen Applications explains how to substitute translated text for different language locales in Zen applications. These concepts apply to Zen reports as well.
Adding Entries to the Message Dictionary
Anywhere that you use the Zen data type %ZEN.Datatype.caption or $$$Text macros in the code for a Zen report, the corresponding text value becomes an entry in the Message Dictionary for that Caché namespace, from which it can be exported for translation. Later, the translated text can be imported back into the Caché Message Dictionary to localize the application.
Important:
Localization works only if the DOMAIN parameter is defined as a non-empty string in the Zen report class. Messages for classes in the same localization DOMAIN are stored together in the Message Dictionary for that Caché namespace.
Many attributes of Zen report components are already of type %ZEN.Datatype.caption and so automatically support localization. Their descriptions in this book indicate when this is the case. You can use $$$Text macros anywhere that ObjectScript is supported, including the values of Zen report runtime expressions. Additionally, Zen report classes offer a shortcut to invoking the $$$Text macros. This shortcut is available only within an XData ReportDisplay block in a Zen report class.
The following syntax:
<item value="@footertime@Created on: " />
Creates a Message Dictionary entry for that Caché namespace with:
Unlike when you use the $$$Text macros, when you use the double-@ shortcut it is your responsibility to ensure that each message identifier ("footertime" in this example) is unique across the localization DOMAIN.
Important:
The double-@ shortcut works only if the special variable %response.Language is set correctly. The way to do this is to place the following statement in the %OnBeforeReport callback method within the Zen report class. This method runs automatically before the report displays:
Method %OnBeforeReport() As %Status
{
  Set %response.Language =
    ##class(%MessageDictionary).MatchLanguage($$$SessionLanguage,"ZenReport")
  Quit $$$OK
}
Localization for Excel Output
The section Configuring Zen Reports for Excel Spreadsheet Output describes how to use Zen reports to create Excel spreadsheets. When you create spreadsheets, the excelName attribute of <element> provides text for column headers in the spreadsheet. When you use a ReportDefinition block that has the required specific structure, excelName supports localization by automatically putting the column header text into the Message Dictionary. When you use the ReportDisplay block to create an Excel spread sheet from XML in an arbitrary format, you must take some additional steps to localize the column header text.
  1. Such reports require the output mode displayxlsx, so you must set DEFAULTMODE or $MODE appropriately. You must also set the DOMAIN parameter. DOMAIN is an arbitrary text string, used to match entries in the Message Dictionary with the classes that generated them. Localization does not work if you have not set DOMAIN.
    Parameter DEFAULTMODE As STRING = "displayxlsx";
    Parameter DOMAIN As STRING = "SKISC"; 
  2. Create a ReportDefinition block that provides XML for the report, such as the one in the following code sample.
    XData ReportDefinition 
    [ XMLNamespace = "http://www.intersystems.com/zen/report/definition" ]
    {
    <report xmlns="http://www.intersystems.com/zen/report/definition"
     name="MyReport" 
     sql="SELECT TOP 10 Name,DOB,Age FROM Sample.Person" 
     runtimeMode="0">
      <group name="Person">
       <attribute name="name" field="Name"/>
       <attribute name="dob" field="Dob" 
        expression="..ToExcelDate(%val)"/>
       <attribute name="age" field="Age"/>
      </group>
    </report>
    }
  3. Define a ReportDisplay block that formats the report for Excel output. The $$$” at the start of the value of excelName marks that text value for localization.
    XData ReportDisplay 
    [ XMLNamespace ;= "http://www.intersystems.com/zen/report/display" ]
    {
    <report xmlns="http://www.intersystems.com/zen/report/display"
     name="MyReport">
      <body>
       <table group="Person" excelSheetName="Persons" 
        excelGroupName="Person" oldSummary="false">
        <item field="@name" excelName="$$$Name" />
        <item field="@dob" isExcelDate="true" 
         excelName="$$$Date of Birth" />
        <item field="@age" isExcelNumber="true" 
         excelName="$$$Age">
        </item>
       </table>
      </body>
    </report>
    }
    The name of the report must be the same in the ReportDefinition and the ReportDisplay.
  4. In order to generate entries in the ^CacheMsg Global for the $$$ expressions, write a helper ClassMethod. It generates the entries during compile of the report:
    ClassMethod Translations() 
    {
        set x=$$$Text("Name")
        set x=$$$Text("Date of Birth")
        set x=$$$Text("Age")
    } 
  5. Export CacheMsg:
    DO ##class(%Library.MessageDictionary).ExportDomainList("C:\Temp\local.xml","SKISC") 
  6. Translate the exported Message Dictionary file, and save the localized versions.
  7. Import your translation.
    DO ##class(%Library.MessageDictionary).Import("C:\Temp\local_de.xml")
Organizing Zen Reports to Reuse Code
In designing a suite of Zen reports, you might discover that you wish to reuse portions of your page design in other reports. Reuse ensures consistency among related reports and generally speeds development. Zen reports offer two ways to achieve reuse within XData ReportDisplay blocks: composites and templates. The primary distinction is that composites can accept parameters at runtime, and templates are entirely static.
The SAMPLES namespace provides a detailed code example in the ZENApp package. The Zen report class ZENApp.CompositeReport.cls defines a report that renders identically to the ZENApp.MyReport.cls example, except that it does so using composites and templates. The ZENApp.CompositeReport package contains several classes that define the composites and templates referenced by ZENApp.CompositeReport.cls.
The following table provides a quick comparison between composites and templates:
Extends XData Block Top Level Container Element For More Information
%ZEN.Report.Display.composite Display <composite> Using Zen Report Composites
%ZEN.Report.Display.reportTemplate (any name) <template> Using Zen Report Templates
Using Zen Report Composites
This topic uses the term composite to describe a block of Zen report syntax that you wish to define separately and then reference repeatedly to provide consistency and reusability for your Zen reports. Like templates, composites can define any part of the XData ReportDisplay block, including elements you would normally place within the <document> <body> or <report> containers. Unlike templates, composites can accept parameters whose values are supplied at runtime. These parameters must be defined as properties of the composite class; you supply values for these properties when you reference the composites in the XData ReportDisplay block of a Zen report class.
The sections in this topic explains all the steps required for this technique to work:
Note:
As long as the set of properties defined in the composite class does not change in any way, a composite class may be modified and recompiled without recompiling the classes that reference the composite. The composite changes are picked up at runtime.
A Zen report composite is a subclass of %ZEN.Report.Display.composite that contains an XData Display block. Unlike templates, the name of the definition block for all composites must be the same: XData Display. Inside the XData Display block is the definition of the composite. This definition is substituted into the code generated by any XData ReportDisplay block that references the composite. In addition to an XData Display block, the composite class can define properties; the purpose of these properties is to allow you to pass values to the composite to modify details of its XData Display definition at runtime.
The sections Creating a Composite to Define Style and Creating a Composite to Define Layout show the parts of a %ZEN.Report.Display.composite class in more detail.
Creating a Composite to Define Style
The following figure highlights key parts of a %ZEN.Report.Display.composite class whose purpose is to define a set of style classes to place within a <document> container. A detailed description follows this figure.
Composite Class for Zen Report Style
Creating a Composite to Define Layout
The following figure highlights key parts of a %ZEN.Report.Display.composite class whose purpose is to define a <pagefooter> within a <report>. A detailed description follows this figure.
Composite Class for Zen Report Display
Referencing a Composite from a Zen Report
The following figure highlights the XData ReportDisplay block of a Zen report class that references several composites, including those defined in the previous sections, Creating a Composite to Define Style and Creating a Composite to Define Layout.” Following this is a detailed description with numbers to match the figure.
XData ReportDisplay with References to Composites
Using Zen Report Templates
Note:
The template feature described in this section is not related to XSLT templates. Compare the section Supplying XSLT Templates to Zen Reports.”
This topic uses the term template to describe a block of Zen report syntax that you wish to define separately and then reference repeatedly to provide consistency and reusability for your Zen reports. Like composites, templates can define any part of the XData ReportDisplay block, including parts of the <document> block and parts of the <body> block. Unlike composites, templates are entirely static; they cannot accept parameters. A template provides simple code substitution.
You can define a display element, such as a <header> or <footer>, as a template. Then you can reference that template when you place the display element inside the <body> block of your XData ReportDisplay definition. The template definition of the element entirely replaces any attributes or other children of the element.
Creating a Zen Report Template
A Zen report template is a subclass of %ZEN.Report.Display.reportTemplate that contains an XData block. Inside the XData block is the template name and definition. The following is an example of a Zen report template class that defines a template called Header1:
Class ZENApp.HeaderTemplate Extends %ZEN.Report.Display.reportTemplate
{
  XData Header1
        [ XMLNamespace = "http://www.intersystems.com/zen/report/display" ]
  {
    <template>
      <header>
        <p class="banner1">HelpDesk Sales1 Report</p>
        <fo><line pattern="empty"/><line pattern="empty"/></fo>
        <table orient="row" width="3.45in" class='table1'>
          <item value="Sales by Sales Rep" width="2in">
            <caption value="Title:" width="1.35in"/>
          </item>
          <item field="@month" caption="Month:"/>
          <item field="@author" caption="Author:"/>
          <item field="@runBy" caption="Prepared By:"/>
          <item field="@runTime" caption="Time:"/>
        </table>
      </header>
    </template>
  }
} 
Templates are only instantiated at runtime. You may use runtime expressions and data items in defining a template; however, any runtime expressions or data items used in a template are evaluated in the context of the report or composite that invokes the template, not the template class itself. The previous template example works only if the data items that it refers to (@month, @author, @runBy, and @runTime) actually exist in the XML data generated by corresponding XData ReportDefinition block.
Referencing a Zen Report Template
The following syntax example references the sample template from the previous section:
<header template="ZENApp.HeaderTemplate:Header1" />
Each element that contributes visible content to the report display — that is, each element that may appear within a <body> block — supports the template attribute. To see a list of display elements, refer to the <report> and <body> sections in the chapter Formatting Zen Report Pages.” The following table describes the template attribute that all of these elements support.
Attribute Meaning
template
Specifies the template that can be used to specify this element, rather than providing attributes and other content directly in XData ReportDisplay. The format for the template value is:
Where:
Generally, when you reference a template, you do not provide any attributes other than template with the element, because Zen ignores the values of any other attributes supplied along with the template attribute. Suppose the <header> element in the previous example also provided a width attribute, as follows:
<header template="ZENApp.HeaderTemplate:Header1" width="7.25in" />
Zen would ignore the value of this width attribute, and instead use whatever width characteristics it found in the Header1 template definition. If it found no width value in the template, it would use the Zen defaults for width, rather than the width attribute provided with this <header> element.
The following sample XData ReportDisplay block shows this sample <header> element in its full context. This example includes a <document> element because the desired output format is PDF:
XData ReportDisplay
      [ XMLNamespace = "http://www.intersystems.com/zen/report/display" ]
{
  <report xmlns="http://www.intersystems.com/zen/report/display"
          name='myReport' title='HelpDesk Sales Report' style='standard'>
    <document width="8.5in" height="11in"
              marginLeft="1.25in" marginRight="1.25in"
              marginTop="1.0in" marginBottom="1.0in"
              referenceOrientation="0">
    </document>
    <body>
      <header template="ZENApp.HeaderTemplate:Header1"/>
      <!-- OTHER ELEMENTS OF THE REPORT DISPLAY -->
    </body>
  </report>
}
Supplying XSLT Templates to Zen Reports
Note:
The features described in this section are specifically related to XSLT templates. For a more general feature that allows you to reuse sections of Zen report syntax in various reports, see Using Zen Report Templates in “Building Zen Report Classes.”
If you are already familiar with XSLT, you might have used <xsl:call-template> syntax to invoke a specific template within an XSLT transformation. <xsl:call-template> permits you to supply parameters and values to the template when you invoke it. Display elements support a similar convention.
In addition to the XData blocks called ReportDefinition and ReportDisplay, Zen report classes support three XData blocks that can contain XSLT templates. The following table lists them.
Use this case-sensitive XData block name… For XSLT templates that apply to…
XData HtmlXslt All XHTML output
XData XslFoXslt All XSL-FO for PDF output
XData AllXslt All output
If you need to use more than one <xsl:template>, you can enclose them in an <zenxslt> element. See the section XData Blocks for <xslt> for more information on <zenxslt>. XData blocks that contain XSLT templates can be in the same report class where they are used, or in a separate report class, where they can be used by a number of reports.
In the following excerpt from a Zen report class, the XSLT templates are empty, so they do not accomplish anything. The example simply shows how to place an XSLT template inside an XData block. You can place any appropriate XSLT statements inside the <xsl:template> container:
XData HtmlXslt {
  <xsl:template name="htmExample" >
  </xsl:template>
}
XData XslFoXslt {
  <xsl:template name="pdfExample" >
  </xsl:template>
}
XData AllXslt {
  <xsl:template name="allExample" >
  </xsl:template>
}
The name provided for the <xsl:template> container in XData HtmlXslt and XData XslFoXslt can be the same name, because these two XData blocks are never used together. If there is an XSLT template that you need for both output formats, place it in XData AllXslt.
Calling XSLT Templates to Apply Styles
One common use for XSLT templates is to call a template to apply styles. The appropriate XSLT template to call depends on the output format. Define a template with the same name in both of the two output-specific blocks:
For example:
XData HtmlXslt
{
  <xsl:template name="redeven" >
    <xsl:param name="num"/>
    <xsl:if test="$num mod 2 = 0">
      <xsl:attribute name='style'>color:red</xsl:attribute>
    </xsl:if>
  </xsl:template>
}
XData XslFoXslt
{
  <xsl:template name="redeven" >
    <xsl:param name="num"/>
    <xsl:if test="$num mod 2 = 0">
      <xsl:attribute name='color'>red</xsl:attribute>
    </xsl:if>
  </xsl:template>
}
With these templates defined, it is possible for the XData ReportDisplay block in the same Zen report class to have an <item> defined as follows:
<item field="@id" width=".7in" 
 style="border:none;padding-right:4px"
 stylecall="redeven" styleparams="@id" 
 styleparamNames="num" > </item>
Where:
The result is that entries with even numbered IDs are colored red. See the table of attributes in the section Report Display Attributes for more information on these attributes.
Calling XSLT Templates While Rendering Items
Note:
The feature described in this section applies only to the <item> element.
To define the XSLT template, place it within an XData block called AllXslt, HtmlXslt, or XslFoXslt in the Zen report class. For example:
XData AllXslt
{
<xsl:template name="sum">
  <!-- Initialize nodes to empty node set -->
  <xsl:param name="nodes"/>
  <xsl:param name="result" select="0"/>
  <xsl:choose>
    <xsl:when test="not($nodes)">
      <xsl:value-of select="$result"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="value" select="$nodes[1]"/>
       <!-- recursively sum the rest of the nodes -->
      <xsl:call-template name="sum">
        <xsl:with-param name="nodes" select="$nodes[position( ) != 1]"/>
        <xsl:with-param name="result" select="$result + $value"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
}
With this template defined, it is possible for the XData ReportDisplay block in the same Zen report class to have an <item> defined as follows:
<item call="sum" params="Sale/@amount" paramNames="nodes">
  <caption value="Total Sales"/>
</item>
Where:
When you obtain the value for the <item> using call, you cannot use the formatNumber attribute to format the result inside the <item> statement. Instead, use the XSLT format-number function inside the <xsl:template> that you are referencing with the call attribute. The following example of an XData AllXslt block shows this convention:
XData AllXslt
{
<xsl:template name="mypct">
    <xsl:param name="num"/>
    <xsl:param name="denom"/>
    <xsl:variable name="v1" select="$num[1]"/>
    <xsl:variable name="v2" select="$denom[1]"/>
    <xsl:choose>
        <xsl:when test="$v2 != 0">
            <xsl:value-of 
    select="format-number(round(($v1 div $v2)*10000) div 10000,'##,###.00%')"/>
        </xsl:when>
        <xsl:otherwise>
             <xsl:value-of select="format-number(0,'##,###.00%')"/>
         </xsl:otherwise>
     </xsl:choose>
</xsl:template>
}
The properties call, paramNames, and params are supported only by the <item> element. See the table of attributes in the section <item> for more information on these attributes.
Conditionally Executing Methods in Zen Reports
As described in the section Zen Report Tutorial,” a Zen report class is also a CSP page. This means that, at runtime, portions of its logic may execute on the server and portions in the browser, as is normal for CSP pages.
By default, Zen report classes process XSLT and generate XHTML output on the server, then ship the results to the browser for display. This behavior is controlled by the XSLTMODE parameter, which is set to "server" by default. You can set XSLTMODE to "browser" if you prefer, but note that when the output format is PDF, processing happens on the server side regardless of the XSLTMODE setting.
When the output format is XHTML and XSLTMODE is set to "browser", the CSP instance for a Zen report communicates twice with the browser, once to generate XML and a second time to generate XSLT. As a result, methods that fit the following description are automatically invoked twice each time you display the Zen report:
To prevent needless performance cost when XSLTMODE is set to "browser", you can program these methods so that parts of them execute conditionally based on the current display mode of the Zen report. The following example accomplishes this for the callback method %OnAfterReport(). This example uses a variety of conventions to check the current display mode, as follows:
Method %OnAfterReport() As %Status
{
 if $IsObject($G(%request))
  {
   set tMode = $ZCVT($G(%request.Data("$MODE",1),$$$GETPARAMETER("DEFAULTMODE")),"L")
   if (tMode="tohtml")
   {
    ; Place the callback logic for XSLT generation here
   }
   elseif (tMode="html")
   {
    ; Place the callback logic for XHTML generation here
   }
  }
  else
  {
   ; Place the callback logic for call from command line here
  }
  Quit $$$OK
}
The following example shows a property with an InitialExpression that is set by calling a method:
Property testProp As %String [ InitialExpression = {..initExpr()} ];
The method itself could be defined as follows:
Method initExpr() As %String
{
 if $IsObject($G(%request))
 {
  Set tMode = $ZCVT($G(%request.Data("$MODE",1), $$$GETPARAMETER("DEFAULTMODE") ),"L")
  if (tMode="xml")
  { 
   set initVal="mode is xml"
  }
  elseif (tMode="tohtml")
  { 
   set initVal="mode is toHtml"
  }
  elseif (tMode="html")
  { 
   set initVal="mode is html"
  } 
  else
   {
    set initVal="all other modes, for completeness"
   }
 }
 else
 {
  Set initVal="called from command line, no potential second round-trip."
 }
 Quit initVal
}
Executing Code Before or After Report Generation
A Zen report is a class that extends %ZEN.Report.reportPage. This base class offers callback methods that you can override in your own Zen report class. Use these callback methods to add any statements that you want Zen to execute before or after it receives the initial HTTP request, generates the XML data source, writes the XSLT stylesheets, creates the report display, or outputs the report in the requested format.
The Zen report callback methods execute automatically as explained in the following table:
Callback Methods in Zen Report Classes
Method Executes Purpose Returns
%OnPreHTTP() After the Zen report receives the initial HTTP request and before %OnBeforeReport(). Execute statements based on the data in the initial HTTP request. If your %OnPreHTTP() method returns 0 (false) report execution stops immediately. %Status
%OnBeforeReport() After %OnPreHTTP() and before Zen begins its main processing sequence to generate the XML data source, write the XSLT stylesheets, and create the report display. Adjust input prior to the main processing sequence. %Status
OnAfterCreateDisplay() Near the end of the main processing sequence, after Zen creates the report display object but before it outputs the display object in the requested format.
Adjust display contents prior to output. You can access individual items using their id attributes with %GetComponentByID(id). For an example, see The id Attribute in the chapter “Formatting Zen Report Pages.”
%OnAfterReport() After Zen completes all report processing and has output the display in the requested format. Clean up after the main processing sequence. %Status