Ask Reuben

Starting with Genero Report Writer (part 2 of 2)

What is the best way to design a report in Genero Report Writer

In the previous article, I went over some key points when starting out with Genero Report Writer.  I want to finish the article and cover a few introductory points when it comes to using the Genero Report Designer.

In my opinion the best way to learn is to have something basic you can experiment and play with.  The code in the 4gl below which can be expanded upon can be used to create data for a simple report.  .  It isn’t a small file so I’ll put it at the end of the article, the key things to note is it

  • has grouping
  • has different datatypes
  • no IF, WHEN in the REPORT block
  • uses GROUP SUM in the AFTER GROUP
  • no PAGE HEADER

… it is concentrating on gathering the data, not laying it out.

You produce the .rdd, that is the data schema of your datastream by compiling that .4gl with the –build-rdd command line option.

Then when you create a new report (step 5, note), (File->New -> Reports -> Empty Report) one of the first things you will do is go into the Data View tab and select the .rdd.

In the Report Structure view it will look like …

… now do some drag and drop in the Report Structure View (Windows->Views->Report Structure) so that it looks like …

… and then Drag and Drop from the Data View into the Report Structure View, the document number and line number fields …

… and then drag and dropping from the ToolBox, add some additional objects so it looks like the below , and with the two Text Boxes you add change the name and the text property values to DocumentNumber and LineNumber respectively.

…if you were to run the report it should look like …

… the key point to note is that this all involved the Report Structure view.  Having a good understanding of the Report Structure and being able to interpret it is a key to being a good report designer.  So what can we tell from the structure

  • Page Root is a child of the document_number trigger so every time document_number changes, a new Page will be started
  • The MiniPage is a child of the OnEveryRow which in turn is a child of the line_number trigger, so every time line_number changes a new MiniPage will be started.
  • These triggers (the red circles) come from the ORDER EXTERNAL BY in the REPORT statement.  Every time there is a new trigger, the child of that trigger will be output.
  • Also note with Report Structure, child is shown by indenting.  LineNumber, and x.line.line_number Value are children of MiniPage as they are indented.  Relative to each other LineNumber, and x.line.line_number Value are siblings.

You may have noticed when dragging and dropping into the report structure view, things might not have gone exactly where wanted.  There is a thin black line that indicates if dropping as child or sibling and if you move slowly up and down as you prepare to drop you should see it.  Dropping on an object will add as a child.

You will see this pattern in your reports.  A trigger for when some data changes and a new block in your report is to be output.  I typically start off a report by just outputting the unique or key fields of each block.  With this structure you can do some clever things, for example guess how you have a different page as the first page. note the FirstPageHeader trigger, add a PageRoot as child of that and that is your first page.  If your 4gl is more complex, you will see red triggers for the IF, FOR etc.

So the first key point I want to make is to learn the Report Structure view.  I have got to the point where I can look at Report Structure view and judge correctness of a report by looking at it.

The second point involves something I briefly mentioned in the last article and that is Relative and Absolute Positioning.  As we dropped elements by dropping them directly into the Report Structure view they are relatively positioned, that is the X and Y values are blank, and in the designer view the object has a green circle.

Now observe what happens if I change the X-Size property value of the LineNumber WordBox to be 6cm  …

… note how its sibling, the x.line.line_number field moved out.  You did not have to move the adjacent field when you resized a field.  By using relative positioning you will find that reports are easier to maintain.

There is a lot of properties for the various widgets and I am not going to cover them all here, I will do some Ask-Reubens at a future date for some interesting ones.

The key thing to making a good start with Genero Report Writer is to have a simple program like this and a simple report from which you can experiment and learn.

A good exercise is to note there are two areas you can drag from on the left, the Toolbox and Dataview, and there are two areas to drop, the Report Structure view and the Design view  or Work area in the centre.  Experiment dragging from the Toolbox and Dataview, note in the Data View the 5 icons at the top you can use to change default values, and experiment dropping in the Report Structure and the Design view, noting the values for X, Y, X-Size, Y-Size and Text or Value.

Another good exercise is with the LayoutNode object find the Section property and change the value to anyPageHeader.  This controls wether the LayoutNode is repeated on every page or just when document number is new.

If you get stuck on a problem, come back to this report program and report, and experiment with it.

So that is my key points, have such a sample program, use and understand the Report Structure view, and understand relative and absolute positioning, and use relative positioning where possible.

#! askreuben70.4gl
IMPORT util

CONSTANT MIN_DOCUMENT_COUNT = 5
CONSTANT MAX_DOCUMENT_COUNT = 20

CONSTANT MIN_LINE_COUNT = 20
CONSTANT MAX_LINE_COUNT = 200

TYPE play_report_type RECORD
    report RECORD
        when DATETIME YEAR TO SECOND
    END RECORD,
    header RECORD
        document_number INTEGER,
        char1 CHAR(1),
        char3 CHAR(3),
        char10 CHAR(10),
        str STRING,
        dec DECIMAL(11, 2),
        int INTEGER,
        dmy DATE
    END RECORD,
    line RECORD
        line_number INTEGER,
        char1 CHAR(1),
        char3 CHAR(3),
        char10 CHAR(10),
        str STRING,
        dec DECIMAL(11, 2),
        int INTEGER,
        dmy DATE
    END RECORD
END RECORD

MAIN

    DEFINE i, j INTEGER

    DEFINE document_count, line_count INTEGER

    DEFINE grw om.SaxDocumentHandler

    DEFINE rpt_data play_report_type

    LET grw = fgl_report_createProcessLevelDataFile("askreuben70.xml")

    IF 1 = 1 THEN
        -- Create Report
        IF NOT fgl_report_loadCurrentSettings("askreuben70_empty.4rp") THEN
            EXIT PROGRAM 1
        END IF
        CALL fgl_report_selectDevice("SVG")
        CALL fgl_report_selectPreview(TRUE)

        LET grw = fgl_report_commitCurrentSettings()
    ELSE
        -- Create Process Level Data File, use this to check data if required
        LET grw = fgl_report_createProcessLevelDataFile("askreuben70.xml")
    END IF

    START REPORT play_report TO XML HANDLER grw

   -- Data unique to report
    LET rpt_data.report.when = CURRENT YEAR TO SECOND
    
    LET document_count = util.Math.rand(MAX_DOCUMENT_COUNT - MIN_DOCUMENT_COUNT + 1) + MIN_DOCUMENT_COUNT
    FOR i = 1 TO document_count

        -- Document Number
        LET rpt_data.header.document_number = i

        -- Random Data
        LET rpt_data.header.char1 = ASCII (util.Math.rand(26) + 65)
        LET rpt_data.header.char3 =
            ASCII (util.Math.rand(26) + 65), ASCII (util.Math.rand(26) + 65), ASCII (util.Math.rand(26) + 65)
        LET rpt_data.header.char10 =
            ASCII (util.Math.rand(26) + 65), ASCII (util.Math.rand(26) + 65), ASCII (util.Math.rand(26) + 65),
            (util.Math.rand(1000000) USING "&&&&&&&")
        LET rpt_data.header.str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

        LET rpt_data.header.dec = util.Math.rand(1000000) / 100
        LET rpt_data.header.dmy = TODAY - util.Math.rand(365)
        LET rpt_data.header.int = util.Math.rand(100)

        LET line_count = util.Math.rand(MAX_LINE_COUNT - MIN_LINE_COUNT + 1) + MIN_LINE_COUNT
        FOR j = 1 TO line_count

            -- Line Number
            LET rpt_data.line.line_number = j

            --Random Data
            LET rpt_data.line.char1 = ASCII (util.Math.rand(26) + 65)
            LET rpt_data.line.char3 =
                ASCII (util.Math.rand(26) + 65), ASCII (util.Math.rand(26) + 65), ASCII (util.Math.rand(26) + 65)
            LET rpt_data.line.char10 =
                ASCII (util.Math.rand(26) + 65), ASCII (util.Math.rand(26) + 65), ASCII (util.Math.rand(26) + 65),
                (util.Math.rand(1000000) USING "&&&&&&&")
            LET rpt_data.line.str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

            LET rpt_data.line.dec = util.Math.rand(1000000) / 100
            LET rpt_data.line.dmy = TODAY - util.Math.rand(365)
            LET rpt_data.line.int = util.Math.rand(100)

            OUTPUT TO REPORT play_report(rpt_data.*)
        END FOR
    END FOR
    FINISH REPORT play_report
END MAIN

REPORT play_report(x play_report_type)
    DEFINE totals RECORD
        dec DECIMAL(11, 2),
        int INTEGER
    END RECORD

    ORDER EXTERNAL BY x.header.document_number, x.line.line_number
    FORMAT
        FIRST PAGE HEADER
            PRINT x.report.*

        BEFORE GROUP OF x.header.document_number
            PRINT x.header.*

        ON EVERY ROW
            PRINT x.line.*

        AFTER GROUP OF x.header.document_number
            LET totals.dec = GROUP SUM(x.line.dec)
            LET totals.int = GROUP SUM(x.line.int)
            PRINT totals.*

END REPORT