Tips   >   Reports   >   Reports (All Contents)

Reports

Writing and trying to control reports in Omnis Studio can be frustrating! In this section I give some tips on some stuff I've learned, but I've got a long ways to go.

When learning to use the report writer, as soon as I wanted to do anything out of the ordinary, I would spend hours upon hours getting lots of different results, rarely the result I wanted.

After wasting far too much time on trying on my own to understand and control reports I gave in and signed up for a training course with David Swain. If you have the opportunity get some training, David Swain has the best understanding of anyone I know on controlling the report writer.

One thing David said to me in the training session, "Don't complain about the report writer in Studio, instead work to understand it". Very true. The coding logic for the report writer in Omnis Studio is different than the coding you do in other areas of Omnis Studio. The reasons for this will become clearer as you work to understand the report writer.

The report writer is complex. Just stop and think about all the different platforms, printers, page sizes, printer trays, etc. etc. that exist on the planet. Now give yourself the job of coming up with a report writer that will work for all of them. Good Luck!

Once you understand the report writer, like the rest of Omnis Studio, you can do pretty well anything you want. Your imagination is the limit. I have a LONG ways to go before I reach that point in Omnis Studio reports.

The report I'm proudest of is a labels report which can print sorted labels side to side or up and down on the label paper and the user can specify which label to start on on the first page, so we can use up the half finished page from the last run.

Good luck learning reports in OMST! Hang in there! Keep a positive attitude! Everytime something goes wrong, remind yourself, "This is an opportunity for me to learn something new." :-)

Background Object Names

Did you know that you can assign names to background objects in reports? Once you have done so, you can then assign methods (like $print, just to give a randon name...) to those background objects. Here's how:

In design mode, put lines for each background object you want to name in the class methods $construct like:

; Assign names the the report background objects
Calculate $cclass().$objs.1004.$name as 'extLine'
Calculate $cclass().$objs.1006.$name as 'otherLine'

Then print the report to the screen. (You don't even need data, just the attempt is enough!) The names for these objects will then appear in both the Property Manager and in the Method Editor.

You can then remove the naming code -- it has done its job.

Thank to David Swain for this tip. If you have a question about reports... David's your man. Visit
www.polymath-bus-sys.com

Dynamic Size Picture Field

If you want to generically print a company's logo a consistent height, centred at the top of each report, the trouble you run into is that the image size, and the image width by height ratio will be different for each company. Some companies might insert a very large image of their logo, others a much smaller image. Some company logos might be square, others rectangular.

In order print each company's logo a consistent height, centred at the top of each report, you will need to dynamically set the picture field size and position in the report header.

The following sample code demonstrates:

  1. Loading a JPEG file into a binary variable.
  2. Converting the JPEG binary to a CS24 picture variable so that it can be printed on a report using a kPicture field.
  3. Sizing the width of the kPicture field so that the width to height ratio matches the image, and then centering the field on the printable area of the report.

Load the JEG file

; Get the logo image.
Do pths.$:DemosFolderPath() Returns DemosFolderPath
If len(DemosFolderPath)
   
   Calculate FolderPath as con(DemosFolderPath,sys(9),'logos')
   Calculate FilePath as con(FolderPath,sys(9),'VencorLogo.jpg')
   
   Do FileOpsExt.$openfile(FilePath,bReadonly)
   Do FileOpsExt.$readfile(iLogoBinVar)
   Do FileOpsExt.$closefile()
   
   Calculate FlagOK as kTrue
   
End If

Quit method FlagOK

Convert the JPEG binary to CS24 Picture binary

; Find out the format of the image.
Calculate Format as pictformat(iLogoBinVar)

; Make sure it is a type we can convert.
Calculate TypesList as pictconvtypes()
If not(TypesList.$search($ref.C1=Format,1,0,0,0))
   ; Log an error.
   OK message (Icon,Sound bell) {Unable to convert a '[Format]' format image for printing on the report.}
   Breakpoint
Else
   
   ; Convert the image to a CS24 picture that can be displayed by the kPicture fields.
   Do pictconvto(Format,iLogoBinVar,'CS24') Returns iLogPictVar
   
   Calculate FlagOK as kTrue
   
End If

Quit method FlagOK

Size and center the picture field on the report.

; Find the width and height of the picture. (In pixels?)
Do pictsize(iLogPictVar,PictWidth,PictHeight)

; Size the width of the picture field in correct ratio to the image and the field height.
Calculate FieldHeight as irLogoField.$height
Calculate FieldWidth as FieldHeight/PictHeight*PictWidth
Do irLogoField.$width.$assign(FieldWidth)

; Centre the logo on the report.
If $cinst.$orientation=kOrientLandscape
   Calculate PrintWidth as ($cinst.$paperlength-$cinst.$leftmargin-$cinst.$rightmargin)*100/$cinst.$scale
Else
   Calculate PrintWidth as ($cinst.$paperwidth-$cinst.$leftmargin-$cinst.$rightmargin)*100/$cinst.$scale
End If
Do irLogoField.$left.$assign((PrintWidth-irLogoField.$width)/2)

Quit method kTrue

Click the Run Demo button in the StudioTips Browser window to see a demo report which does this. The report name is rDynamicLogo. Feel free to copy the report class and/or code to your own libraries.

Fixed Record Section Height

If you want a fixed height for your report's record section you set the $printheight property of the kRecord section divider on the report.

This works fine if you don't have any kPosition dividers inside the kRecord section. As soon as you add a kPosition divider in the record section the $printheight property is ignored. (Working with Omnis Studio 4.2.0.6 - I reported this problem to Tech Support)

The work around is to add a kPosition section at the end of the record section and set it to start at a position that puts it at the bottom of the record section.

Each kPosition section is cumulative from the previous section object. In my situation I needed a print height of 3.5.

The end kPosition was set to 1.250 from top of previous section for a total of height of 3.5

Note

The response I got from Tech Support was as follows: It must be noted that Positioning sections also have a $printheight property, so you need to make the sum of the positioning sections and record section $printheight equal to 3.5 inches in this case.

Horizontal lines

Do you have horizontal lines in any of your reports? When you are moving or aligning fields in the report, the horizontal lines always seem to be getting in the way of the selection marquee.

A trick I use is in design mode to create 1 cm (1/2") long horizontal lines and place them in the correct line of the report but off the right side of the page. In the $construct of the report I then use $sendall to position and stretch the horizontal lines across the full width of the report.

; setHorzLines (method called by $construct)

; Position and stretch $objtype=kLine which are off the right side
; of the report to the full width of the page.
; This allows the lines to be 1 cm long and off the right side in design mode)

; If the report is reduced or enlarged, we have to adjust the line length proportionally
If $cinst.$orientation=kOrientPortrait
   Calculate Width as ($cinst.$paperwidth-$cinst.$leftmargin-$cinst.$rightmargin)*100/$cinst.$scale
Else
   Calculate Width as ($cinst.$paperlength-$cinst.$leftmargin-$cinst.$rightmargin)*100/$cinst.$scale
End If
Do $cinst.$objs.$sendall($ref.$width.$assign(Width),$ref.$objtype=kLine&$ref.$left>Width)

; Slide the lines over to the left margin.
Do $cinst.$objs.$sendall($ref.$left.$assign(0),$ref.$objtype=kLine&$ref.$left>Width)

Quit method kTrue

The &$ref.$left>Width in the $sendall argument prevents other horizontal lines from being stretched and repositioned by the $sendall.

Load page setup Information

The following explanation on Report Page Setups was done by Rudolf Bargholz. Thanks to Rudolf for permission to include it in StudioTips.

Problem
-------
When reports to different printers one is often faced with the situation:

a) How is the printer set up?
b) What orientation is the report to be printed in?
c) How does one ensure that some reports will get sent to one printer, others to another, without
having to specify the printer each time?

Solution
--------
What is a page setup? Basically it is the settings a printer is to use when printing a document. See the screenshot
below for an example:

Screenshot of a Studio page setup dialog for a printer

As can be seen, all reports sent to this printer will be printed in Portrait, the paper size is A4, etc. Some printers allow one to change more settings, for example label printers.

There are numerous places where page setup information can be stored. Omnis can store a page setup in a report. One can also store and use a page setup defined by a specific printer. On windows platforms one has a Printer folder in which all the installed printers can be viewed. These printers each have their own printer/page setup. Omnis has a specific logic that allows one to mix and match these page setups with different reports, i.e. one can print reports based on the page setup stored in a report or use the settings defined under Windows. The logic Omnis uses is ...

1) when you start Omnis and print a report, the page setup of the default printer of that computer will automatically be loaded and used for printing all subsequent reports.

2) if you use the command 'Select printer', explicitly specifying the parameter 'Discard previous settings', Omnis will load the page setup of that specific printer, i.e. the settings entered under the properties of that printer in the Printer folder in Windows. This is a windows specific attribute.

Select printer (Discard previous settings) {HP LaserJet 5000 GN PCL 5e,FILE:}
will then use the printer properties I set up under Windows for this printer driver.

3) you can explicitly load the page setup of a report using the command 'load page setup'. Note that this page setup will be used in the printing of all reports until a new page setup is loaded or the page setup of a printer is loaded as described in 2) above. The code below uses the page setup of the report 'r1':

Set report name r1
Load page setup
Send to printer
Select printer {HP LaserJet 5000 GN PCL 5e,FILE:}
Print report

This concept of a page setup is important to understand. A report can store page setup information.

Set report name r1
Prompt for page setup

will open a dialog in which one can choose printer settings, e.g. the orientation of the paper, the printer to which the report is to be printed, etc. When one closes the dialog with Ok, the settings are stored in the report. When one then says

Set report name r1
Load page setup

one loads the printer settings of that report into memory. Every report one prints from now on will use the printer settings of the report 'r1'.

Set report name r1
Load page setup
Print report
Set report name r2
Print report

In this case both reports 'r1' and 'r2' will be printed using the page setup of the report 'r1'.

Let us look at the case where one has two printers. Some reports are to be printed to one printer, other reports to the other printer.

HP LaserJet 4
- Report1 (portrait)
- Report2 (landscape)
- Report3 (portrait)
- Report4 (landscape)

Zebra Label Printer
- Report5
- Report6

In this case one can do the following: define three new EMPTY reports. These reports are simply dummy reports that hold the page setup information for the specific printers.

The end user has to perform the following code ONCE:

Set report name MyLaserJet4DummyReport_Portrait
Prompt for page setup

Set report name MyLaserJet4DummyReport_Landscape
Prompt for page setup

Set report name MyLabelDummyReport
Prompt for page setup

Each time the customer saves the page setup for that specific printer WITH THE DUMMY REPORT. Now,
whenever I wish to print to the LaserJet 4 in Portrait the following code can be used:

Set report name MyLaserJet4DummyReport_Portrait
Load page setup
Set report name Report3
Print report

If I wish to print to the Label printer the following code can be used:

Set report name MyLabelDummyReport
Load page setup
Set report name Report5
Print report

Remember, that Omnis uses the page setup one has loaded for each report until one loads a new page setup of another report or loads the page setup information for a specific printer.

The following will fail:

Set report name MyLabelDummyReport
Load page setup
Set report name Report5
Print report
Set report name Report3
Print report

'Report3' is supposed to be printed to a LaserJet 4 in portrait mode; the code is telling Omnis to
print 'Report3' to the printer based on the printer settings stored in the report 'MyLabelDummyReport', the label printer, which is not correct. The code won't fail to function but it will print the report that was meant to go to the LaserJet to the Label printer; all the text in the report will get printed out one lots of little labels. To get the code to work correctly, it has to be edited:

Set report name MyLabelDummyReport
Load page setup
Set report name Report5
Print report

Set report name MyLaserJet4DummyReport_Portrait
Load page setup
Set report name Report3
Print report

The reports 'MyLabelDummyReport' and 'MyLaserJet4DummyReport_Portrait' are simply printer groups with predefined properties that can be loaded when needed for another report. It is important to remember that the printer name is stored with the page setup when one uses 'Prompt for page setup' and 'Load page setup'. It is because of this fact that one does not have to specify which printer one wants to print to when one loads a page setup of a report. Sometimes this can cause problems. The developer might not have the same printer as the customer. The report works fine at the developers, at the customer's site the report fails to print correctly. The reason for this is, that the page setup of the developer is used to print the report and the report tries to print to a non-existent printer. The customer must 'Prompt for page setup' once to set up the page setup of the report once with the settings of HIS printer. Note, that if the customer changes his printer or wants to print to another printer, he will have to 'Prompt for page setup' again.

Another situation can arise when an application is used in a large company that share a library but have many printers. Some people print a specific report to one printer, others print the same report to another printer. For each printer it is advisable to have one dummy report. One then has to adjust one's code in such a manner to account for this complexity in the organization. It might be advisable to store a list in the database, which user prints which report to which printer.

There are other really cool things one can do with Omnis reports in Omnis Studio. For example, we want the second page of a report to be landscape, the following to be portrait. In your page footer place the following code in the $print method:

Switch #P
Case 1
Calculate $cinst.$orientation as kOrientLandscape
Case 2
Calculate $cinst.$orientation as kOrientPortrait
End Switch

Do default

This will print the first page using your page setup, the second page explicitly in landscape, the following pages in portrait.

Another point to remember is the 'job setup' with which it is possible to determine the tray to which the first page is printed as well as the tray to which the other report pages are printed.

On evClick ;; Event Parameters - pRow( Itemreference )
Set report name r1
Prepare for print (Ask for job setup) {r1}
End print

Page Count External

Need to print "Page x of y" on your report page headings? Where x=current page, and y=total pages in the report.

Omnis Studio provides a great external object in the F3 Component Store that does this for you.

  1. Open the report in design mode.
  2. Press F3 to open the Component Store.
  3. In the Component Store toolbar, click the External Components icon.
  4. Drag the Page Count (#) object from the Component Store into your report header.
  5. Select the page count component. Press F6 Property Manager.
  6. Set the various properties to suit your requirements. ($prefix, $maintext, $fieldstyle, etc.)

Report Subtotals

To generate subtotals for a column in a report you must set include the column in the $sorts group of the report and set the $subtotal property to kTrue.

To do this in a report class:

  1. Click the Sort Fields button in the report class header. This opens the Sort fields window.
  2. Enter iList.ColName in the Field Name column and set the Subtotals column to True.
  3. Close the Sort fields window.
  4. Right-click on the report class background and select Properites....
  5. Click the Sections tab in the Property Manager.
  6. Set $subtotal1 to kTrue. The subtotal number corresponds to the line in the Sort fields list.
  7. Add whatever fields you like to the Subtotals section which has been added to the report class.
  8. If you want a field in the subtotal section to display its subtotal, average, maxiumum, minimum, or count, you set the $totalmode of the field accordingly in the Property Manager.

You can access the subtotal value of any field in the subtotal section using the notation iList.ColName.$total (or $average, $maximum, $minimum, $count)

Click the Run Demo button in the StudioTips Browser window to test a report class which has subtotals.

Send to screen

By default I send reports to the screen so that the user has an opportunity to review the report before deciding whether or not to print the report.

Issuing the Send to screen command causes all reports that follow to be sent to the screen. In my applicaitons I normally issue a Send to screen command somewhere during the $construct of the application's Startup_Task.

If somewhere in the application I have a report which is to be sent directly to the printer, I'll put the Send to printer command in a reversible block.

Begin reversible block
Send to printer
End reversible block

The Send to screen command has several parameters

Click the Run Demo button in the StudioTips Browser window to test the following Send to screen test code.

Set report name rScreenReportSize

Send to screen (Do not wait for user) No Size Parameters
Print report

Send to screen (Do not wait for user) Instance A /25/50/800/600
Print report

Send to screen (Do not wait for user,Hide until complete) Instance B /50/75/800/600
Print report

Quit method kTrue

Send to screen - Title

There are two different ways you can control the title which appears in screen reports.

  1. Omnis Command - The first parameter of the Send to screen command sets the report title. Whatever you specify will appear as the title.

    Set report name rScreenReportSize

    Send to screen (Do not wait for user) Report Title/25/50/800/600
    Print report

    Quit method kTrue

  2. Notation - The $prefs.$windowprefss property is where the last Send to screen setting is stored. You can directly assign the $windowprefs property using notation.

    Do $prefs.$windowprefs.$assign('Notation Report Title /25/50/800/600')

The preference is global so when you set it, either using the Omnis command or notation, it affects all future reports that get sent to the screen, until a different value is set.

Test for Printer

If a printer has not been chosen, it causes a problem for reports. The problem occurs so infrequently that when it happens it often takes an hour to solve. To avoid this problem, always test for printer before running a report. (It only takes a nano second)

@SAMPLECODE:1

Dynamic Report Page Header

Ask yourself the following questions:

  1. Do the report page headers contains the same or very similar information on 80% of your reports?
  2. Do you find it redundant to add and position the same page header fields on 80% of the reports?

There is a relatively easy solution for this. Leave the page header blank in all your reports that use a standard page header and add the page header fields and text to each report instance using notation. Omnis Studio can add your standard page header fields to a report instance in the blink of an eye! The beauty of adding report page header fields on-the-fly is that if you want to make a change to the page header in your standard reports, you only have to change the code in one place and presto, all of your standard reports will immediately reflect the change.

As with anything in Omnis Studio there are many ways to accomplish the above. For this solution we are going to structure the code as follows:

  1. The object class oReportTools contains the code which adds fields to the report instance page header.
  2. The report class must contain an invisible linemarker field to indicate the page header line where a field is to be added. If the linemarker does not exist, the field will not be added.
  3. The report class rPageHeader_template contains the template fields which will be copied to the report instance by oReportTools. Using a template report class makes it easy for the developer to modify any of the page header fields.

The sequence of event for this solution is as follows:

  1. The target report instance is opened.
  2. Omnis Studio sends a $construct message to the report instance.
  3. The $construct method of the report sends a $constructPageHeader($cinst) message to oReportTools.
  4. The $constructPageHeader method find the rPageHeader_template report class and then searches for the appropriate invisible linemarker fields in the target report instance and copies the corresponding field from the template report class to the specified line on the target report instance.
Instructions and code for the above solution are included with this tip.

Page Header Template

To make it easy for you to edit the fields for your standard page header we will create a page header template report class.

  1. Create a new report class.
  2. Name the report class rPageHeader_template
  3. Add a Page Header section to the report.
  4. Add each of the standard page header fields you use on your report page headers. Name each field with name that is easy to read and understand. e.g. PageCount, Title, SubTitle, PrintInfo, PageHeaderNotes.
Click the View Report Class button in the StudioTips Browser to see the rPageHeader_template report class. Free free to copy the report class from StudioTips to your library.

Report Tools Object Class

The report tools object class contains the code which adds the template fields to the report instance.

  1. Create a new object class.
  2. Name the object class oReportTools
  3. Add a $constructPageHeader method to the object class with the following code.

    ; $constructPageHeader (method)

    ; Figure out the page report width.
    If pfrReportInst.$orientation=kOrientPortrait
       Calculate Width as (pfrReportInst.$paperwidth-pfrReportInst.$leftmargin-pfrReportInst.$rightmargin)*100/pfrReportInst.$scale
    Else
       Calculate Width as (pfrReportInst.$paperlength-pfrReportInst.$leftmargin-pfrReportInst.$rightmargin)*100/pfrReportInst.$scale
    End If

    ; Find the page header template report class.
    Do $clib.$classes.$findname('rPageHeader_template') Returns irTemplateRpt
    If irTemplateRpt
       
       ; Print Info
       Calculate FieldName as 'PrintInfo'
       Do method addTemplateField (pfrReportInst,FieldName) Returns rField
       If not(isnull(rField))
          
          Do rField.$left.$assign(0)
          
          ; Page Count
          Calculate FieldName as 'PageCount'
          Do method addTemplateField (pfrReportInst,FieldName) Returns rField
          If not(isnull(rField))
             
             Do rField.$left.$assign(Width-rField.$width)
             
             ; Company Name
             Calculate FieldName as 'CompanyName'
             Do method addTemplateField (pfrReportInst,FieldName) Returns rField
             If not(isnull(rField))
                
                Do rField.$left.$assign(0)
                Do rField.$width.$assign(Width)
                
                ; Title
                Calculate FieldName as 'Title'
                Do method addTemplateField (pfrReportInst,FieldName) Returns rField
                If not(isnull(rField))
                   
                   Do rField.$left.$assign(0)
                   Do rField.$width.$assign(Width)
                   
                   ; SubTitle
                   Calculate FieldName as 'SubTitle'
                   Do method addTemplateField (pfrReportInst,FieldName) Returns rField
                   If not(isnull(rField))
                      
                      Do rField.$left.$assign(0)
                      Do rField.$width.$assign(Width)
                      
                      ; Page header notes
                      Calculate FieldName as 'PageHeaderNotes'
                      Do method addTemplateField (pfrReportInst,FieldName) Returns rField
                      If not(isnull(rField))
                         
                         Do rField.$left.$assign(0)
                         Do rField.$width.$assign(Width)
                         
                         Calculate FlagOK as kTrue
                         
                      End If
                   End If
                End If
             End If
          End If
       End If
    End If
    Quit method FlagOK

  4. Add an addTemplateField private method to the object class with the following code.

    ; addTemplateField (private method)

    If len(pLineMarkerName_opt)=0
       Calculate pLineMarkerName_opt as con(pFieldName,'_linemarker')
    End If

    ; Find the linemarker
    Do pfrReportInst.$objs.$findname(pLineMarkerName_opt) Returns rMarker
    If rMarker
       
       ; Find the template object.
       Do irTemplateRpt.$objs.$findname(pFieldName) Returns rSourceField
       If rSourceField
          
          ; Add the field to the report.
          ; $add(type[,cComponentLibrary,cComponentControl],iTop,iLeft,iHeight,iWidth) inserts a new object and returns an item reference to it
          If rSourceField.$objtype=kComponent
             
             Calculate Lib as rSourceField.$componentlib
             Calculate Control as rSourceField.$componentctrl
             Do pfrReportInst.$objs.$add(kComponent,Lib,Control) Returns rField
             If rField
                
                Do rField.$name.$assign(rSourceField.$name)
                Do rField.$fieldstyle.$assign(rSourceField.$fieldstyle)
                
                Do rField.$lineno.$assign(rMarker.$lineno)
                Do rField.$left.$assign(rSourceField.$left)
                Do rField.$width.$assign(rSourceField.$width)
                Do rField.$height.$assign(rSourceField.$height)
                Do rField.$align.$assign(rSourceField.$height)
                
             End If
             
          Else
             Do pfrReportInst.$objs.$add(kEntry) Returns rField
             If rField
                
                ; Copy all of the source field attributes to the target added field.
                Calculate rField as rSourceField
                
                Do rField.$fieldstyle.$assign(rSourceField.$fieldstyle)
                Do rField.$lineno.$assign(rMarker.$lineno)
                
             End If
             
          End If
       End If
    End If
    Quit method rField

Click the View Object Class button in the StudioTips Browser to see the oReportTools report class. Free free to copy the object class from StudioTips to your library.

Note

The oReportTools object class in StudioTips includes a $setHorzLines method which can be called to dynamically position and stretch the horizontal lines across the report paper width.

Dynamic Header Report Class

Each standard report class will need to include the page header lines and appropriate linemarker fields for each standard page header field you want to include in the report page header.

  1. Create a new report class.
  2. Name the report class whatever name is appropriate for the particular report.
  3. Add a Page Header section to the report.
  4. Add invisible linemarker fields off the left side of the report class page. Name each linemarker with template field name and the suffix _linemarker. e.g. PageCount_linemarker, Title_linemarker.
  5. Add the following code to the $construct method of the report class.

    ; Construct the standard page header.
    Do ioReportTools.$constructPageHeader($cinst) Returns FlagOK



    The ioReportTools instance variable must be pointed to the the oReportTools object class.
Click the View Report Class button in the StudioTips Browser to see the rDynamicPageHeader report class. Free free to copy the report class from StudioTips to your library as a starting point for your report classes.