List-Based Reports
By David Swain
Polymath Business Systems
So far in this series of articles
on Omnis Studio reports, we have considered some basic information
about report classes and the default process Omnis Studio follows
to generate simple reports from its native database. This time we
will look a bit closer at how Omnis Studio can generate reports
from lists. We will do this for the simplest of cases for exploratory
purposes, so the techniques we use here may not necessarily be the
ones you'll want to use in finished applications.
Besides creating a report directly from an Omnis database, Omnis
Studio can also create a report directly from the contents of a
list variable. The report class itself has a property named islist.
If the value of this property is set to kTrue, we are telling
Omnis Studio to perform the report in a slightly different way.
It does not have to query the database for the contents of the report
(at least not the main contents), but simply use record images already
gathered into a list. So along with setting the islist
property value to kTrue, we must also supply the name of
an in scope list variable as the value of the mainlist
property of the report class instead of supplying the name
of a File Class as the value of the mainfile property.
These properties can all be found under the General tab
of the Property Manager for the Report Class.
There are some interesting mechanics behind how Omnis Studio uses
the list content for a report set up in this way. Understanding
how various aspects of the report and the report process are affected
by this can help us when we attempt to build more complex reports.
Simplest Case
Let's begin this investigation with the most basic case: a report
based on a globally in-scope list variable, such as a hash variable
like #L1, defined from globally in-scope variables from a File Class.
(I can hear gasps from some "advanced" developers. Have
patience - we're starting at the beginning...) What we want to look
at here is the behavior of Omnis Studio in using this list variable
in a report instance.
In this case, we build the list externally to the report and then
print the report. We want to look at how Omnis Studio handles such
things as sorting and searching and what effect these things have
on both the report output and the original list variable.
We will build our list with a few columns from a File Class named
memberFile. To house this process and act as the base for
other experimentation, we will build a Window Class named reportLauncher
with two pushbutton fields on it as shown here:
The method to build our list (from a native Omnis
database) is quite simple and can be done in a number of ways. Here
is one of them. Put the following code into the $event
method for the Populate #L1 pushbutton (use any File Class
you happen to have handy):
I have chosen just a few columns from this File Class for a reason.
I have also deliberately built the list in RSN order. We'll see
why as we continue with the experiment. Now let's quickly create
a Report Class named testReport1 with a 1-line Record
section and a field for each column in our list variable. A fast
way to do this is to drag variable names from the Component Store
onto the Report Editor window. It should look something like this
when finished:
Be sure to set the islist and mainlist property
values as shown earlier in this article. Now we need a method for
printing this report. In the $event method for the Print report
pushbutton on our launcher window, put the following code:
If we now instantiate the launcher window, we can click the Populate
#L1 pushbutton to put content into our list variable. Using
the debugger to examine the content of this variable (by context-clicking
on the name of the variable in the Catalog window and selecting
the first line of the context menu, for example), we will see something
like this:
The record images are in RSN order. If we print our report, they
should be in the same order by default. Clicking on the Print
report pushbutton should give results like this:
Now that we have our basic report working, we can do some more
exploring...
Sorting the Report Content
We know that Omnis Studio reports can sort their content based
on some predefined "Sort Field" information, so let's
see how list-based reports handle this. We can open the Sort Fields
window for the Report Class (only) by clicking on the Sort Fields
icon in the Report Editor toolbar. Let's set the sort information
as follows:
If we print the report again, the results are as we would expect:
Great! But what about the original contents of our list variable?
Not a problem! Through the magic of the Sort Buffer, the original
list is not touched. (Go ahead and check...) Let's consider this
for a moment. The most likely scenario as to how Omnis Studio and
the Sort Buffer may be operating is this: The Sort Buffer is established
separately with a column for each sort field and another for the
line number in the original list variable (instead of the RSN value
as with a File-based report). This information is then be merged
into the Sort Buffer list, sorted and then the original list is
simply queried in the sorted order for the generation of the report.
In support of this theory (since the Sort Buffer has not been exposed
to us), let's perform another experiment. Place a field to the left
of the others in the Record section of the report. Set
the calculated property value to kTrue and the
text property value to #L1.$line. Now launch the
report again. The results should look something like this:
If the original list had been sorted for the report, the line numbers
would be sequential beginning with 1. Remember, line numbers in
a list variable belong to the container, not the content.
There is no way we can know for sure exactly how the Sort Buffer
is used. The creators of Omnis Studio have not exposed such things
as the Sort Buffer to us (and maybe I just made it up!). But the
good news is that we can print list-based reports where the report
imposes a specific sort order and the original list order will remain
untouched after the report is completed. Omnis Studio does everything
in its power to protect the original contents of our list.
Filtering the Report Content
We know that we can use a Search Class or Search Calculation to
limit the records included in a report when deriving our content
from a native Omnis database. We set the current Search Class or
Search Calculation and then use the Use search option of
our printing command. But can we do something similar with a list-based
report? Time for another experiment!
Change the $event method behind the Print report
pushbutton to read as follows:
If we instantiate our window and click the pushbutton - we get
the same report as before. The contents of the list are not
filtered when we invoke the Use search option. Perhaps
Omnis Studio is just trying to protect us in some way, but we really
need this report to be filtered. We have three choices if we want
to filter such a report:
- We can use a search when building the list in the first place
- We can switch on the smartlist property for the list
variable, filter it before printing the report and then unfilter
it afterward
- We can apply the search criteria one record at a time in the
$print method of the Record section of the report.
This third option could use an example. If we put the following
code into the $print method of the Record section of the report,
it will test the contents of the report's process buffer to see
whether it satisfies the search criteria. If no search is in force,
all records are printed.
Notice that we are checking to see whether either a Search
Class (sys(81)) or Search Calculation (sys(89))
is in force. If we do this, the Use search option of the
Print report command is not needed. (It wasn't helping
us anyway.) On the other hand, this can interfere with other more
advanced techniques we may wish to apply later. So I am not offering
this as a total solution, but only pointing out that there is a
workaround for this case.
Thinking Outside the List
Speaking of workarounds, there is another important one to discuss
before finishing this article. A significant drawback to the use
of the list-based reports that we have examined so far is that they
are limited to the contents of the list variable on which they
are based. With the list contents that we have been using, we cannot
build a report that uses other content from each memberFile
record. I hear experienced Omnis developers clearing their throats
in the background and I know they are about to say that we can just
use an autofind field to read in the entire record or do
a Single file find in the $print method for the
same purpose. While this technique works great for reading in related
records based on foreign key values that happen to be in the list
already, it can not be used on primary key values within
the list.
For example, we have a column for memberFile.idNumber,
the primary key for memberFile, in our list. Perhaps we
want to include in our report the first name and telephone number
(and even additional address information) for each record. We can
place additional fields on the Report Class layout and make the
idNumber field an autofind field (by setting its
autofind property value to kTrue), but this will
not give us the desired results. Even if we make this field calculated
and give it either "memberFile.idNumber" or "#L1.idNumber"
for its text value, it makes no difference to the resulting
report. It appears that Omnis Studio is simply protecting the values
being reported from the list variable and not allowing them to be
"altered" - even though we are only replacing them with
the same values. (Single file find in a $print
method doesn't work either - for the same reasons.)
Again, we can piece together a workaround, but (also again) I do
not necessarily endorse this as a technique for finished applications.
We can make a duplicate of the File Class from which our
list was originally defined, set that File Class to Memory only
(since we only want to use its variables) and redefine
the list using the corresponding columns in this "shadow"
class just before printing the report. Since the Redefine list
command is not reversible, we must also re-redefine the list immediately
after printing so any list display field will still display values
for us. We can then use either of the techniques mentioned in the
previous paragraph (calculated autofind field on the report
layout or Single file find command in the $print
method of the Record section) to fulfill our mission. The
calculation must be based on either the new File Class name or the
name of the list as the qualifier for the column name. Here is an
example of the modified Print report code:
Next Time
I hope this little exploration has been enlightening and useful
for you. So far we have only worked with a list variable that is
global. Next time we will begin to look at how we can use an instance
variable list in a report, either generated internally within the
report instance or passed to it through a parameter of the $construct
method. We are getting closer to working with viable techniques
we can use in finished projects! |