Tech News Back Issues Issue: 110404
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.
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:
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:
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!
Copyright of the text and images herein remains with the respective
author. No part of this newsletter may be reproduced, transmitted,
stored in a retrieval system or translated into any language in any
form by any means without the written permission of the author or
Omnis® and Omnis Studio® are registered trademarks, and Omnis 7 is a trademark of Raining Data UK Ltd. Other products mentioned are trademarks or registered trademarks of their corporations. All rights reserved.