Variables and Totals
Variables are the report engine’s built-in accumulators. They provide counts, sums, averages, and other aggregate values such as group totals, page totals, running balances, and grand totals. The basics of creating a variable in the Designer are shown in the "Variables and Groups" section of the Getting Started chapter, and the XML format is listed in the Specification part. This chapter explains how variables work at run time: when they are updated and reset, what each section of the report sees, and how to build typical totals with them. The last section is the complete reference of the built-in system variables.
How variables work
A variable is defined once for the whole report, in the <variables> element of the report definition. In the Designer, select
and edit the variables in the table of the dialog (columns ID, Expression, Function, Scope, and Init Value).
<variables>
<variable id="total_value" type="num" funcType="sum" scope="group" initValue="0">value</variable>
<variable id="balance" type="num" funcType="sum" scope="report" initValue="0">value</variable>
<variable id="rows" type="num" funcType="count" scope="page" initValue="0">ProductID</variable>
</variables>
- id
-
The name of the variable. In expressions and templates, the variable is referenced as
$V{id}; in a field, set the field source type to Variable (ftype="var") and enter the ID as the column. - funcType
-
The aggregate function:
count,sum,average,std,min, ormax(case-insensitive). An unknown name is read ascount. - scope
-
The reset scope:
report,page, orgroup(case-insensitive). An unknown name is read asreport. - initValue
-
The value the variable starts from and returns to whenever it is reset.
- type
-
The data type. Variables are always numeric (floating point) in the engine; the Designer writes
type="num", and the attribute is not evaluated when the report is loaded. - Element text
-
The variable expression: the source of the value that is aggregated (see The variable expression).
Function types
Each time the variable is updated, the current value of its expression is passed to the aggregate function.
| Function | Result |
|---|---|
|
The number of updates, that is, the number of processed rows (the expression value itself is not used, but it must be a valid column). The initial value is the starting count. |
|
The sum of the values. |
|
The arithmetic mean of the values processed since the last reset. |
|
The standard deviation of the values processed since the last reset. The engine calculates the population standard deviation (the squared deviations are divided by n, not n-1). |
|
The smallest value. The initial value setting is ignored: a |
|
The largest value. The comparison starts from the initial value, so if all values can be negative, set the initial value to a number
smaller than any expected value (the default |
The variable expression
The variable expression can take two forms:
- Data source column
-
The name of a column of the detail’s data source, for example
value, or a column of a specific data source in thedatasourceID.columnform, for exampleDataSource1.value. Note that the column is written without the$D{}reference syntax. The variable is then bound to that data source (see Variables in reports with more than one detail). - Script expression
-
If the expression contains the
<%and%>tokens, the text is evaluated like a field expression: the data source, parameter, and variable references are substituted, the embedded script is executed, and the result is converted to a number. TheVariable_Expression.ncrdemo report sums a calculated ratio this way:<variable id="calculations" type="num" funcType="sum" scope="report" initValue="0"><%$D{width}/$D{height}%></variable>In the Designer you simply type
<%$D{width}/$D{height}%>into the Expression column; the XML escaping is done automatically.
|
A variable based on a script expression is not bound to any data source, so the automatic page break correction described in Evaluation timing does not apply to it. Use a plain column expression for page totals and carried-forward balances whenever possible. |
Reset scopes
The scope determines when the variable returns to its initial value:
- Report
-
The variable is reset only when the report starts. It accumulates through the whole report: use it for grand totals and running balances.
- Page
-
The variable is reset at the beginning of every page, before the page header is printed. Use it for page totals.
- Group
-
The variable is reset after the footer of a group is printed, but only if the variable is listed in that group’s Reset variables list (the
resetVariablesattribute of the<group>element). AGroupscope variable that no group resets behaves like aReportscope variable.
In addition, all variables are reset when the report starts (in double pass mode, at the start of both passes), and when the engine moves to the next detail section, the variables bound to the data source of the new detail are reset, whatever their scope.
|
In the Designer, only |
Evaluation timing
All variables are updated once for every data row, before the row is printed. For each row of a detail section, the engine performs the following steps:
-
If the value of a group expression has changed, the footers of the closed groups are printed (the innermost group first). The footers still see the variable values of the previous row, that is, the totals of the closed group. After each footer, the variables listed in that group’s Reset variables list are reset.
-
All variables are updated with the current row.
-
If a new group starts, the headers of the new groups are printed. During the group header, the update made by the current row is temporarily taken back, so a group header shows the value before the first row of the group (
0for a freshly reset group variable). -
The detail section is printed. The detail sees the value including the current row, so a
ReportscopeSUMvariable printed in the detail is a running total.
The page and report level sections see the following values:
| Section | Value of the variables |
|---|---|
Report header |
The initial values (the first row is temporarily taken back). |
Page header |
The values after the page reset: |
Detail |
The values including the current row. |
Group header |
The values before the first row of the new group. |
Group footer |
The values including the last row of the group. |
Page footer |
The values including the last row printed on the page. |
Report footer |
The final values. Note that |
The page break needs special care, because when the engine finds that a detail row does not fit on the page, the variables have already
been updated with that row. In this case the engine steps back one row and takes back the last update of the variables bound to the
current data source, prints the page footer, begins the new page (resetting the Page scope variables and printing the page header),
and only then reapplies the update. As a result, the page footer of the old page does not contain the row that moved to the next page,
and the row is counted in the Page scope variables of the new page. The same correction is applied when a group header does not fit
on the page.
This correction is controlled by the Data trimming report option (the dataTrimming option element), which is on by default. If you turn it
off, the page footer and the page header of the next page show values that already include the first row of the next page.
|
The correction can take back only the last update. With |
Variables in reports with more than one detail
By default, every variable is updated for every row of every detail. A variable bound to a column of another data source is updated with
that data source’s current value, which is rarely what you want. If the report has more than one detail section, turn on the
updateOnlyCurrentDataSourceVariables report option: then a variable bound to a data source column is updated only while a detail of the
same data source is being processed. Variables with a script expression are always updated.
To get an alert when a variable expression cannot be evaluated (for example, a misspelled column name), turn on the varErrorAlert report option;
otherwise an invalid variable is silently left unchanged.
Page totals and running balances
Invoices, ledgers, and long lists often need three kinds of totals on every page: the total of the current page, the running balance up to the end of the page, and the balance carried over from the previous page at the top of the next page. With the timing rules above, all three need only two variables:
-
a
PagescopeSUMvariable for the page total, printed in the page footer; -
a
ReportscopeSUMvariable for the running balance, printed in the page footer as the "carried forward" value and in the page header as the "brought forward" value.
The PageTotalsAndBalanceInProductlist.xml demo report prints the running balance of the value column in the detail, in the page footer, and
in the page header. The page_total_example.xml demo does the same in a grouped product list, and prints the grand total in the footer of a
constant (%CONST) group. The following example is based on these demos, extended with a page total variable. Geometry and font attributes are
omitted.
<variables>
<variable id="page_total" type="num" funcType="sum" scope="page" initValue="0">value</variable>
<variable id="balance" type="num" funcType="sum" scope="report" initValue="0">value</variable>
</variables>
<pageheader id="phead">
<label id="1">Brought forward:</label>
<field id="2" type="num" ftype="var" formatting="true" format="f" precision="2"
printWhen="$V{pageno} > 1">balance</field>
</pageheader>
<pagefooter id="pfoot">
<label id="1">Page total:</label>
<field id="2" type="num" ftype="var" formatting="true" format="f" precision="2">page_total</field>
<label id="3">Carried forward:</label>
<field id="4" type="num" ftype="var" formatting="true" format="f" precision="2">balance</field>
<field id="5" type="num" ftype="sys" arg="Page: %1">pageno</field>
</pagefooter>
<details>
<detail id="Detail1" datasource="DataSource1">
<items>
<field id="1" type="num" ftype="ds">ProductID</field>
<field id="2" type="txt" ftype="ds">ProductName</field>
<field id="3" type="num" ftype="ds" formatting="true" format="f" precision="2">value</field>
<!-- running balance in every row -->
<field id="4" type="num" ftype="var" formatting="true" format="f" precision="2">balance</field>
</items>
</detail>
</details>
Suppose the first page holds rows with the values 10, 20, and 30, and the next row (40) does not fit. The report then prints:
| Place | page_total |
balance |
|---|---|---|
Page 1 header |
0 |
0 (hidden by the print condition) |
Page 1 detail rows |
10, 30, 60 |
10, 30, 60 |
Page 1 footer |
60 |
60 (carried forward) |
Page 2 header |
0 |
60 (brought forward) |
Page 2 first detail row |
40 |
100 |
The printWhen expression of the brought-forward field hides it on the first page, where there is nothing to bring forward. The same
technique works for counts (COUNT) and for group level running totals: a Group scope variable printed in the page header shows the
running total of the current group up to the end of the previous page.
|
In the Designer, set the Reset scope to |
Group totals and nested group totals
A group total is a Group scope variable that is reset by its group. Since the reset happens after the group footer is printed, the
footer shows the total of the whole group, and the next group starts from the initial value again. The GroupFunc.ncr demo, for example, sums the
col0 column by month:
<variables>
<variable id="total" type="num" funcType="sum" scope="group" initValue="0">col0</variable>
</variables>
...
<group id="Group0" groupExp="$MONTH{col1}" resetVariables="total" reprintHeader="false" startsOnNewPage="false">
<groupheader id="GH_Group0">...</groupheader>
<groupfooter id="GF_Group0">
<field id="0R2HF" type="num" ftype="var">total</field>
</groupfooter>
</group>
For nested groups, define one variable for each level, each with the same expression, and let every group reset only its own variable. The groups
of a detail are nested in the order in which they are listed: the first group is the outermost one. When an outer group changes, all of the inner
groups are closed too, so the inner footers are printed first (and reset their variables), then the outer footer. The group_simple_totals.xml
demo uses a constant outer group for the grand total and an inner group for the subtotals:
<variables>
<variable id="total_0" type="num" funcType="sum" scope="group" initValue="0">price</variable>
<variable id="total_1" type="num" funcType="sum" scope="group" initValue="0">price</variable>
</variables>
...
<groups>
<group id="Group0" groupExp="%CONST" resetVariables="total_0">
<groupheader/>
<groupfooter>
<field id="1" type="num" ftype="var">total_0</field> <!-- grand total -->
</groupfooter>
</group>
<group id="Group1" groupExp="groupID" resetVariables="total_1">
<groupheader>...</groupheader>
<groupfooter>
<field id="0" type="num" ftype="var">total_1</field> <!-- subtotal of the groupID group -->
</groupfooter>
</group>
</groups>
The resetVariables attribute is a comma-separated list, so one group can reset several variables, for example a sum and a count of the same
group (resetVariables="sum_price,count_items"). A variable must not be listed in an outer group only, or the inner footers would show the
accumulated value of the outer group.
Other useful patterns:
-
A row number restarting in every group: a
COUNTvariable reset by the group and printed in the detail. Thevariable-row-index.ncrdemo uses this technique. -
A group average: an
AVERAGEvariable reset by the group, printed in the group footer. -
A group total printed in the page header and footer: the
GroupTotals.xmldemo printsGroupscope variables in the page header, the page footer, the group headers (where they show the value before the first row of the group), and the group footers.
Modifying variables
In special cases, the regular aggregation is not enough: you may want to add an extra amount to a total, subtract a value under a
condition, or restart a counter in the middle of a group. For this purpose, the report engine contains a variable modifier
(the NCReportVariableModifier class), which changes the current value of a variable directly.
The modification lines are written into the conditional formatting code of a Label or a Field (see Conditional Formatting). A line that starts
with $V{ is not a style definition but a variable modification, in the following form:
$V{variableID} operator expression
The available operators are:
| Operator | Effect |
|---|---|
|
Sets the variable to the value of the expression. |
|
Adds the value to the variable. |
|
Subtracts the value from the variable. |
|
Multiplies the variable by the value. |
|
Divides the variable by the value. |
The expression on the right side can contain data source, parameter, and variable references, and it is evaluated by the script engine,
so it can be a constant, a reference, or a script expression such as $D{price}*$D{qty} or ($D{discount}>0 ? 1 : 0). A COUNT variable
is set to the integer part of the result.
The modification is executed when the item is printed: each time the section that contains the item is rendered and the item’s print condition (Print only when expression is true) is true. Therefore the print condition of the item acts as the condition of the modification. The new value is visible for the items rendered after the modifying item in the same section, and for all later sections. The variable’s own aggregation continues from the modified value at the next row.
The Variable-Modify-By-CondFormat-Demo.xml demo report has three Report scope variables. The label in the detail section changes its
font weight conditionally, and also adds 2 to the v3 variable in every row:
<variables>
<variable id="v2" type="num" funcType="sum" scope="report" initValue="0">col0</variable>
<variable id="v3" type="num" funcType="sum" scope="report" initValue="0">col0</variable>
<variable id="v1" type="num" funcType="count" scope="report" initValue="0">col0</variable>
</variables>
...
<detail id="Detail1" datasource="DataSource1">
<items>
<label id="1" cf="1">
<tr>Lorem ipsum </tr>
<cf>font-bold:{if($D{col1}>20) true; else false;}
$V{v3}+=2
</cf>
</label>
<field id="N7Q30" type="txt" ftype="var">v1</field>
<field id="HTPFP" type="txt" ftype="var">v2</field>
<field id="ROE94" type="num" ftype="var" formatting="true" format="f" precision="2">v3</field>
</items>
</detail>
The v2 and v3 variables sum the same column, but because the label is rendered before the v3 field, the v3 column shows the running
sum plus 2 for every row printed so far (in the n-th row, v2 + 2 * n).
Keep the following rules in mind:
-
The
$V{must be at the very beginning of the line, and every modification must be on its own line. The style lines and the modification lines can be mixed in any order; the style evaluation skips the modification lines. -
The operator is identified by the first of the strings
+=,-=,/=,*=, and=that occurs in the line. If you use the=operator, the expression must not contain any further=character (for example==,>=, or<=); use another form, or move the condition into the print condition of the item. -
An unknown variable ID or an empty expression makes the engine skip the rest of the modification lines of the item.
-
The modification is applied every time the item is printed. If the section is printed more than once (for example, a section with automatic height that did not fit on the page and is printed again on the next page), the modification is applied again. The automatic page break correction (see Evaluation timing) does not take back a modification.
System variables
System variables are values provided by the report engine itself, such as the page number, the current date, or the name of the application. They are read-only and do not need to be defined. A system variable can be used:
-
in a field with the System variable source type (
ftype="sys"), entering the name as the column; -
in a field with the Variable source type, in an expression, or in a template, with the
$V{name}syntax, for examplePage $V{pageno} of $V{pagecount}.
With the $V{} syntax, the name is first looked up among the report’s own variables, so a user variable with the same name hides the system
variable. The names are case-sensitive. An unknown name returns the text #VAR#.
The following table lists all of the system variables recognized by the report engine.
| Name | Value | Notes |
|---|---|---|
|
The current page number of the report. |
At least 1. Restarts in every report of a batch, in every forced copy, at a group with
the Reset page number option, and, if the |
|
The total number of pages of the report. |
Available only in double pass mode (see "Double pass mode") and in Text Document mode;
otherwise it returns |
|
The global page number. |
Counted continuously across the reports of a batch (see Batch Report Mode) and across forced copies. Without batch mode
and forced copies it equals |
|
The sequence number of the current report in a batch, starting from 1. |
|
|
The number of reports in the batch. |
|
|
The number of forced copies. |
Set by |
|
The number of the copy being rendered when forced copies are used. |
Zero-based: the first copy is |
|
The current row number of the detail. |
Zero-based in normal reports: the first row of a detail is |
|
The number of groups of the current group level printed so far. |
Meaningful only in group headers and group footers; the counter belongs to the group of the section being printed. It is not offered in the Designer’s system variable list; type the name manually. |
|
The current date. |
|
|
The current time. |
|
|
The current date and time (timestamp). |
|
|
The name of the report engine: |
Constant of the NCReport library, not of your application. |
|
The long name of the report engine: |
Constant of the NCReport library. |
|
The version number of the NCReport library, for example |
Constant of the NCReport library. |
|
The full info string of the report engine: name, version, release name (Commercial or Evaluation Version), and copyright. |
Depending on the build, the text may end after the version number. |
|
The copyright notice of NCReport. |
Constant of the NCReport library. |
|
The name of the host application. |
|
|
The version of the host application. |
|
|
The organization name of the host application. |
|
|
The organization domain of the host application. |
|
|
The operating system: |
Determined at compile time. Empty on other platforms. |
|
The version of the Qt library at run time. |
|
|
The current language code of the report. |
See "Internationalization". The default (no translation) language code is |
|
The |