Sw4   >   Reports   >   Reports (All Contents)

Reports

Reports tends to be the last thing we complete in our applications, yet it is often the output that users and their managers are looking for from the application.

Writing and modifying reports tends to be a tedious low level job, not one of my favorite jobs, but one that has to be done.

The oReports object in the StudioWorks framework attempts to simplify report writing by auto-generating reports based on the meta-data of the SQL list of records being passed to the object.

For very simple reports, the auto-generated report does the trick. If you need a more complex report (e.g. calculated fields, subtotals, multiple lines per record, ...) you can let StudioWorks auto-generate a report for you and then modify the report class which was created by StudioWorks.

This section explains how to setup up and modify reports in the StudioWorks framework.

The following diagram gives you an overview of the classes and sequence of messages for printing a report.

PrintReportSeq.jpg

Although it may seem complex, the class structure actually reduces the amount of code you need to write, and makes it possible to reuse code for a web application.

Reports Menu

The Reports menu can be made to appear in any window instance toolbar. The Reports menu lists the reports which are available to the user from a particular window instance.

Each report that you create needs to be assigned a unique report instance ID. The string table translation of the ReportInstID is used for the report menu line text and the report title.

To add a report to a window instance's Reports menu:

  1. Select the Report Instances tab of the Programmer Workbench.
  2. Right-click on a root node and select New ReportInstID...
  3. Enter the report instance ID in the prompt and click the Okay button.
  4. StudioWorks added the report instance to the root node. There are additional fields you can edit.

    The reportinstsuffix property lets you declare a single base report instance with multiple sub-report instances. An example would be a report that has a couple different classes for different sorting and subtotal options. The base report class might be rBooksList which sorts by the book title. A second rBooksList_SortByTypeSeriesTitle. They are both called the BooksReport instance, but the second one has the report instance suffix SortBySeriesTitle. See the section on Report Suffixes for more information.

    The menulineid property lets you declare a menu line ID that is different than the reportinstid for the report. By default the menulineid matches the reportinstid.

    Mapping of the reportinstid to a menulineid is useful if the user selects the Report Properties... menu line of the Reports menu. Any menulineids in the current Reports menu that match menulineids in the report instances list will appear in the Modify Report Properties window.

    The securitycheckrequired property is a future enhancement hook, where the system administrator could specify which report instances a group or user is permitted to run. Currently, the security is based on the window instance. If a user can access the window instance, they have access to all the reports specified for that window instance. To date this level of security has been sufficient.

Reports Menu Observer Object

The oReportsMenuObserver object is classified as a visual object. It is responsible for prompting the user for any input values or choices that are needed prior to running the report. (Date from, Date to, select from a list of records, etc.)

Once the object has the necessary user input criteria it sends a message with the necessary parameters to the non-visual oPrintReport object in the same library. The oPrintReport object fetches the records, and all going well sends a $printReport message to oReports.

The oReportsMenuObserver object is also responsible for notifying the user if an error is returned by oPrintReport.

The reason for splitting the visual and non-visual code between oReportsMenuObserver and oPrintReport is so that the oPrintReport code can be reused by a web app if you decide to add web app functionality to your application.

If there are no user prompts required when running a particular report, you can simply redirect the message to oPrintReport as shown in the following code snip.

; No prompts required. Redirect the message to oPrintReport.
Do $cinst.$redirectToPrintReportObj($cmethod) Returns FlagOK
Quit method FlagOK

Tip

The oModelessPrompts object, instantiated by the task variable modelessprmpt is a helpful friend any time you need to prompt the user for input. Use oModelessPrompts rather than Prompt for input.

Print Report Object

The oPrintReport object is a non-visual object responsible for gathering the list of records to be printed on the report and then sending a message to the oReports object which handles the actual printing of the report.

The method gets a SQL defined list, prepares a WHERE statement, and asks the table class to get the records. Once it has the records it passes the list of records along with other parameters to the oReports object.

How you accomplish fetching the records is up to your own creativity. The following is a snip of some code which used in an oPrintReport object method:

; Print a list of all the active books in the library sorted by Type/Series/Title.

; Get a defined list based on a query class used for books reports.
Do lsts.$retDefinedList('qBooksReport') Returns ReportList

; Prepare the SQL text WHERE clause.
Calculate SQLText as con("WHERE TypeGroupName = 'Books' AND Book.Active = 1")

; Call the custom $getWhere tBase table class method.
Do ReportList.$getWhere(SQLText) Returns FlagOK
If FlagOK
   If ReportList.$linecount
      
      ; Self call $_printReport which calls oReports. We could call oReports direct.
      Calculate ReportInstID as 'BooksReportSortByTypeSeriesTitle'
      Do $cinst.$_printReport(ReportList,ReportInstID) Returns FlagOK
   Else
      ; This isn't really an 'error', but the error handler is a convenient way to communicate this to the user.
      Calculate Mssg as "No records found matching the query."
      Calculate Dtls as SQLText
      Do errhndlr.$logMinorError($cmethod,Mssg,Dtls)
      Calculate FlagOK as kFalse
   End If
End If
Quit method FlagOK

Reports Object

The oReports object is the central dispatcher for printing reports. You aren't required to use this object for printing reports, however, you should try to use it because the object has some helpful techniques for printing reports and automatically adding report and page headers which can then be set by user defined report preferences.

The oReports object, of the swReports4 library is instantiated using the startup task variable rprts.

The oReports object has public methods which you can use to print reports. The code inside oReports can get quite complicated to follow, so for now we'll stick to the basics.

The simplest method is $printDefaultReport. You can pass in a SQL defined list of records and this method will generate a report which prints the includeinlist columns of the SQL defined list.

Do rprts.$printDefaultReport(pReportsList,pReportTitle_opt,pReportInstID_opt) returns FlagOK

If you don't pass in a ReportInstID, the method calculates the ReportInstID as the base table of the ReportList plus the suffix List. e.g. BookList.

If you don't pass in a report title, the method calculates the ReportTitle as the Tblplural string table translation of the report list's base table.

The oReports object looks for a report class which matches the name of the ReportInstID prefixed with the letter r. eg. rBookList. If found it prints the report using the report class. If not found, the oReports object creates a temporary report class with the prefix X_, indicating that it is a temporary class. e.g. X_rBookList. The temporary report class is created in the same library as the SQL class of the ReportList.

After a temporary report has been printed, you can manually remove the X_ prefix from the report class name and then customize the report adjusting fields, adding subtotal sections, adding sorting, etc. The next time you print the same report, the oReports object will find your customized report and use it rather than generating its own report.

Report Class

The rReport_abstract report class, located in the swReports4 library is the superclass used by the StudioWorks framework for report classes. It is recommended that you subclass your report class from rReport_abstract. Auto-generated reports are automatically subclassed from rReport_abstract.

The main feature of rReport_abstract is that is creates a standard report page header on-the-fly. The header can include: date and time printed, user who printed the report, page number of, company logo, company name, company contact info, page header notes. A multitude of property methods control displaying or hiding the various header objects, and whether or not they print on the first page only. You can review the public methods using the Interface Manager on the rReport_abstract class.

If you study the $construct method you will see that it receives the following parameters: pfList, pExtraParamsRow, prCallBackInst, pCallBackMethod.

This gives you two ways to control the report which is being printed:

  1. The extra parameters row is processed by the $_processExtraParamsRow method. If you study that method you will see that the method loops through the columns in the row and assigns each row value using a property method matching the column name. You can pass in addition information by adding a property method and a $assign property method to the report class, and adding a column to the extra parameters row by the same name. (Do not include the $: prefix in the column name)
  2. The prCallBackInst is sent a pCallBackMethod message after processing the extra parameters row. If you pass in reference to an object class instance and a method name, that method will be called before the report instance begins printing. A reference to the report instance is passed to the call back method. The call back method now has full control of the report instance and can call any of the property method or public action methods of the report instance.
Normally, you don't have to worry about the extra parameters row and the call back parameters. The oReports object methods takes care of them for you. If you need extra control or need to pass additional information to a custom report you will want to use either or a combination of the above parameters.

Report Properties

Users often want some level of control over how the report prints out. As developers we like to give users this control so they don't need to ask us for minor changes to reports.

The Reports menu includes a Report Properties... menu line which lets the user control the report page header objects of reports which are subclassed from rReport_abstract and are declared in the oReports object.

After you create a custom report and it shows up in the Reports menu you can select the Report Properties... menu item. This opens a window where the user can modify the report page header and some of report presentation properties.

Note

If the report instance doesn't show up in the window check to make sure it has been added to the oReportsList object in the module.

One report property I find especially useful is the Page Header Note. Users can enter the names of people who are to receive a copy of the report. The names appears on the report header. If the list of people changes the user can update the Page Header Note at any time and it immediately shows the new list of people the next time the report is printed.

Report Suffixes

Report suffixes can be useful if you have several slighty different versions of the same basic report. The following example describes a situation where we could use report suffixes.

We have a Books report that is sorted by Title/Author or by Author/Title. Even though both versions of the report has the same information, using the same report class would be difficult because we want the Title in column one for the Title/Author sort, and the Author in column one for the Author/Title sort.

To use report suffixes we would do the following:

  1. Declare a ReportInstID of BooksReport.
  2. Create a report query class, qBooksReport. The query will be used for both reports.
  3. Create a report class, rBooksReport_SortByTitleAuthor.
  4. Create a report class, rBooksReport_SortByAuthorTitle, by copying rBooksReport_SortByTitleAuthor and then modifying the duplicate.
  5. Add BooksReport to the sMn_stb schema class with the description Books Report for translation of the report title.
  6. Add SortByTitleAuthor to the sMn_stb schema class with the description Sort by Title/Author for translation of the report subtitle.
  7. Add SortByAuthorTitle to the sMn_stb schema class with the description Sort by Author/Title for translation of the report subtitle.
  8. Add BooksReport to the oReportsList.$addReports method. This declares the report instance in the module.
  9. Add BooksReport to the oMenusList.$addReportsMenuLines method. This adds the report instance to the reports menu of the window instance you specify.

When the user selects Reports > Books Report , a $BooksReport message will be sent to the oReportsMenuObserver object. In that method you could prompt the user for which books they want to include in the report and whether they want the report sorted by Title/Author or Author/Title. You then pass this information to the $BooksReport method of the non-visual oPrintReport object. The oPrintReport method then fetches the records and sends a $printReport message to oReports with the necessary parameters.

Report suffixes are optional. Using them can reduce the amount of code you write for slightly different versions of the same report. The oPrintReport protected methods $_getReportRecordsAndPrintReport and $_printReport and the oReports method $printReport support report suffixes in their parameters.

There are actually 2 types of report suffixes:

  1. Report class suffix is used to calculate the report class that is used for the report. The report class is calculated as con('r',ReportInstID,'_',ReportClassSuffix). The report class suffix does not affect the default report title which is the string table translation of the ReportInstID.
  2. Report instance suffix which is use to calculate the full report instance ID. The full report instance ID is calculated as con('ReportInstID,'_',ReportInstSuffix). The report index suffix does affect the default report title which will be the string table translation of the full report instance ID. Use a report suffix if you want different report titles for the different versions of the report.
Report suffixes do not affect the default query class used by the $_get... methods in oPrintReport. The default query class is calculated as con('q',ReportInstID).

Detailed and Summary Reports

The StudioWorks framework makes it easy for you to print the same report class as a summary report or a detailed report.

Typically you need this when management wants a report with just the summary information, but staff needs the same report with both the summary and detailed information.

To accomplish this in StudioWorks:

  1. Start with a report class that was auto-generated by StudioWorks or was copied from rReportBuilder_template.

    The report class should have a kPositioning section named Record details inside the record section. The $printif property of this section will be set to iPrintDetailed. Based on the value of iPrintDetailed, the lines inside the Record details positioning section will either print or be skipped.

    The report class must be a subclass of rReport_abstract.
  2. Position the summary report fields above the Record details section record line in the report class.
  3. Position the detailed report fields below the Record details section record line in the report class.
  4. Before printing the report prompt the user with a radio button dialog asking them to choose Summary or Detailed.
  5. Declare an ExtraParamsRow variable in your method and add a boolean column named, printdetailed.
  6. Set the value for the column to true for detailed, or false for summary.

    ExtraParamsRow to the report. (There is a pExtraParmsRow_opt parameter in oPrintReport.$_printReport, and oReports.$printReport.)

The $_processExtraParamsRow method of the report class sets the ivar iPrintDetailed in the report class to the value you pass via the pExtraParamsRow_opt parameter. If no value is passed StudioWorks defaults to false.

You can extend this technique to other sections of your report by using the iPrintDetailed in the $printif property of the section.

The following is a snip of code which prompts the user with a radio button input and then puts the selected value into an extra parameters row variable.

; Prompt the user to indicate what records they want to select for the report.

Calculate PromptTitle as 'Report Type'
Calculate Mssg as "Select the type of report you wish to run."

; RadioGroupLabel_kRadio_Radio1Label_Radio2Label_
Calculate DetailsRadioGroupLabel as 'Report Type_kRadio_Summary_Detailed'
Calculate PromptButtons as 'Cancel,Continue'

; prmpts.$promptInputVars(pMessage,pTitleStbNameID_opt,pIconName_opt,pInfoMessage_opt,pCSVButtonStbIDsLeftToRight_opt,pCSVButtonModesLeftToRight_opt,pInputLabel1,pfInputVar1,...)
Do $ctask.prmpts.$promptInputVars(Mssg,PromptTitle,cPromptIcon,,PromptButtons,,,DetailsRadioGroupLabel,cPick_Summary_Detailed) Returns ButtonPressed
If low(ButtonPressed)<>'continue'
   Quit method kTrue
End If

; Pack the 'PrintDetailed' variable into an extra parameter row which gets passed to the report.
Do ExtraParamsRow.$cols.$add('printdetailed',kBoolean) Returns ColRef
Calculate ExtraParamsRow.[ColRef().$name] as cPick_Summary_Detailed