Introduction and Tutorial


Contents:
Welcome to COBOL Report Writer
Introduction to this Product
What is COBOL Report Writer?
Compatibility With Built-In COBOL Report Writer
Gentle Introduction
What is a Report?
What Does COBOL Report Writer Do?
COBOL Report Writer in Easy Steps
More about COBOL Report Writer
More about Files and Reports
More about the RD Entry
More about CONTROLS
More about TYPEs
Automatic Repetition
More about Totalling
More about Conditional Entries
Some Shorter Forms
Other Features
Variable-Length Fields
Insertion Characters
COLUMN CENTER and RIGHT
NEXT GROUP Clause
GROUP LIMIT Clause
CONTROL HEADING at Top of Every Page
MULTIPLE PAGE Groups
Line WRAP
FUNCTIONs
Special Print Attributes (Styles)
Independent Report File Handlers
Multiple Reports
Using the Page Buffer

This first part is a short introduction to the principles of COBOL Report Writer.  After reading it, you will be able to write or maintain simple report writer code and you will have enough appreciation of the more advanced concepts to be able to locate the information quickly in the main parts.

All the information given here can also be found in the more formal context of Parts 2 to 5.




Welcome to COBOL Report Writer

 



Introduction to this Product




This product has two separate purposes:

 ¶   To improve programmer productivity in all aspects of printed output in COBOL by encouraging both experienced users and newcomers to make more use of COBOL's report writer feature.

 ¶   To help users who have had experience with a version of COBOL report writer that was an integral part of the compiler and want to continue to use the same facilities.


The report writer features are implemented in this product by means of a precompiler, rather than within the compiler itself.  The compiler processes the intermediate source which the precompiler automatically passes to it.  The precompiler phase is made as far as possible transparent to programmers, so that their attention is not distracted from the original report writer source.  The precompiler and the compiler cooperate closely in a single-step operation and a final listing phase combines the output from both to produce a single source listing, enabling you to disregard the fact that two separate processes are involved.  A description of this process will be found in Installation and Operation.



What is COBOL Report Writer?



COBOL Report Writer is COBOL's own built-in non-procedural facility for the production of printed output.  It enables you to define and produce all the listings, reports, and displayed summaries that would normally be required from a COBOL application, but in far less time.  It allows many more printed outputs, which might have been produced previously using stand-alone non-COBOL report generators, to be done in COBOL, because it reduces greatly the time and effort needed to code and test a COBOL program with printed output.

Report writer appeared in its original form in 1961 and later entered the 1968 ANS Standard.  This version provided certain basic features that users of accounting machines were accustomed to, such as simple accumulation, cross-footing, and counter-rolling, as well as automatic page numbering.  The implementation of report writer described in this volume contains all the facilities of the standard ANS-68, ANS-74, and ANS-85 report writer, plus IBM extensions, and includes many additional features which were added in various stages since 1974, many of which appear in the proposed ANS-9x standard.  It is more suitable for the more varied and complex outputs needed by modern applications.



Compatibility With Built-In COBOL Report Writer

COBOL Report Writer includes the whole of the ANS-68, ANS-74, and ANS-85 report writer standards, so if you have been using a COBOL compiler that has report writer built in, your programs should work just as they did before.

COBOL Report Writer also has many completely new features that are not a part of these standards, as well as enhancements to the original features.  A summary list of all the enhancements will be found at the start of parts 2, 3, and 4, and in Appendix A.  Those ANS-68 features that were deleted or changed in the ANS-74 and ANS-85 Standards are nevertheless retained in this product; these retentions are also listed in Appendix A.





Gentle Introduction



What is a Report?

Wherever you see the term report in this publication, it means any human readable output that may be produced by a program.  Nowadays, the term report is normally used to mean a special printout or screen produced by a report generator.  We use the term in a more general sense.  Any readable output, whether long or short, "one-shot" or routine, printed or not, is a report.  For instance, any of the following is a report and could be produced by COBOL Report Writer:

 ¶   Pay slips and paychecks printed on a mainframe printer;

 ¶   Invoices printed by a small remote printer;

 ¶   A small summary print produced at the end of a large update program;

 ¶   Sales of golf shoes, summarized by region and area, during the years 1985 to 1992 (a one-time, ad hoc report);

 ¶   An extremely complex print of personnel records with many variable-length lines and fields, "printed" on microfiche.

The only requirements for a report are that it should be readable (all fields USAGE DISPLAY only) and should consist of output only.



What Does COBOL Report Writer Do?



When you write report writer code, you do not write a sequence of procedural statements as you would in elementary COBOL.  Instead, most of your effort is spent in specifying the appearance of the report.  The DATA DIVISION syntax enables you to code the layout of your printout entirely in descriptive data-oriented terms.  The only "verbs" used are those that begin (INITIATE) and end (TERMINATE) the report and that GENERATE whole blocks of lines, known as report groups.

Report writer automatically generates your print record descriptions, your intermediate data areas, and all the procedural code needed to produce your outputs, saving you the effort that elementary COBOL would have required.  For particularly difficult or challenging layouts, there are more advanced data clauses.  By studying these in the later parts of this publication, you will learn to produce all your outputs with COBOL Report Writer.

Although so much is performed automatically, you still retain control at the highest level over all operations, because no report writer action takes place until one of the statements INITIATE, GENERATE, or TERMINATE is executed.  However, these statements are sufficiently high-level to require only the simplest logic in your PROCEDURE DIVISION.

Since you may use COBOL Report Writer in any COBOL program, you may use it in any program that has to produce readable output - even if the program performs many other tasks.  COBOL Report Writer does not extract the input data itself, unlike a report generator, which means that it may be used in partnership with all types of COBOL input: standard files, databases, and subroutine or module linkage.


COBOL Report Writer in Easy Steps



Step 1:   Find the Report Groups

Your program may have one or several report layouts.  Here is an example of one hypothetical report layout:

            CRUMBLY COOKIE COMPANY   ORDERS   PAGE 1

       DATE     TYPE       QUANTITY     VALUE OF ORDER

    10/04/84    GINGERBREAD   100           $20.50

    06/05/84    CHOC. CHIPS    50           $18.20

    11/06/84    LEMON CREAM   150          $110.00
  **OUT OF STOCK


               TOTALS: DEPOT NORTH-WEST    $148.70
                                         ====== ===

Your first task is to divide up the layout into report groupsA report group is a "block" of lines, produced in one operation.  Your layout may be built up from any number of different report groups.  You can allow the shape and contents of each report group to vary as much as you like but, if the variations become very complex, it will be easier to define two different report groups.  The following guidelines should be used to define a report group:

 ¶   It may consist of from one up to any number of lines, and may have any number of fields.

 ¶   It normally fits on one page, rather than being split by a page boundary.  There are exceptions to this rule in MULTIPLE PAGE groups and REPORT HEADING and FOOTING groups, described later.

 ¶   It may contain fields whose contents come from anywhere in the DATA DIVISION, provided that all the fields are present in memory at the moment your program generates the report group.

If your report structure corresponds to records in a main file or database, remember that, unless you have a special reason for reading ahead and buffering several records, a report group should correspond to one record from your main file or database.  (However, there is also a summary reporting feature that enables your program to output one report group that summarizes a whole set of records.)

Mark each report group clearly.  Only one instance of each group needs to be marked, because only one description of each group is needed.  You might use square brackets in the margin of your layout.  In this example, let's draw a "box" round each report group.

Here is the result:

             CRUMBLY COOKIE COMPANY   ORDERS   PAGE 1

       
DATE     TYPE       QUANTITY     VALUE OF ORDER
(A)

    10/04/84    GINGERBREAD   100           $20.50 (B)
     06/05/84    CHOC. CHIPS    50           $18.20 (B)
    11/06/84    LEMON CREAM   150          $110.00
  
**OUT OF STOCK
(B)

               
TOTALS: DEPOT NORTH-WEST    $148.70
                                         
=========

(C)


There are three instances of report group (B) in the picture.  Only one instance needs a "box", and it is best to draw it around the most complex case, that is, the instance with the extra line "**OUT OF STOCK".  (We want this line to be part of the same report group, rather than a report group in its own right, because we want to ensure that it will never be separated from the preceding line by a page advance.)



Step 2:   Decide on the TYPE of Each Report Group

Each report group can appear in one of seven basic positions in your report, indicated by the TYPE clause.  Here are their names and positions:

DETAIL or DE
This is the TYPE assumed by any group that is not of one of the special six described below.  DETAIL groups usually contain the most basic data in the report.  They are the only report groups that you GENERATE.  TYPE DETAIL and the next two are known as body groups.  (They fall between the PAGE HEADING and PAGE FOOTING, if any, on each page.)

CONTROL HEADING or CH
This group is generated automatically at the start of each different value of the corresponding control field (as explained in Step 3 below).

CONTROL FOOTING or CF
This group is generated automatically at the end of each different value of the corresponding control field.

PAGE HEADING or PH
This group will appear at the start of each page.

PAGE FOOTING or PF
This group will appear at the end of each page.

REPORT HEADING or RH
This group will appear once, on a page by itself or before the first PAGE HEADING (if any), at the very start of the printout.

REPORT FOOTING or RF
This group will appear once, on a page by itself or after the last PAGE FOOTING (if any), at the very end of the printout.

Each TYPE is optional.  Your report may contain any number of different DETAIL groups, any number of different CONTROL HEADING and CONTROL FOOTING groups (up to one of each for each control level), but only one of each of the other four.

We can now assign the correct TYPEs to each group in our layout:

             CRUMBLY COOKIE COMPANY   ORDERS   PAGE 1

       
DATE     TYPE       QUANTITY     VALUE OF ORDER
 TYPE PH

    10/04/84    GINGERBREAD   100           $20.50  TYPE DE
     06/05/84    CHOC. CHIPS    50           $18.20  TYPE DE
    11/06/84    LEMON CREAM   150          $110.00
  
**OUT OF STOCK
  TYPE DE
                TOTALS: DEPOT NORTH-WEST    $148.70
                                         
=========
  TYPE CF


Step 3:   Code the RD Entry

Your report groups are described in the REPORT SECTION, which is the last section in your program's DATA DIVISION.  The REPORT SECTION may contain any number of Report Descriptions.  Each of these begins with an RD entry that starts in the A-margin:

 
        REPORT SECTION.
        RD

Follow this with a report-name of your choice.  This name will be used to stand for the report as a whole, so choose a name that is appropriate:

 
        REPORT SECTION.
        RD  STOCK-SUMMARY

Several clauses may follow your report-name.  The optional LINE LIMIT clause gives the maximum number of columns you expect per line and is used as a safety measure against losing data due to line overflow.  The FIRST DETAIL clause (or its alternative spellings FIRST DE or FIRST BODY GROUP) indicates on which line the main information of each page should start.  The PAGE LIMIT clause is required if your report is divided into pages.  It gives the maximum number of lines to be written to each page.  The order in which you code these clauses and phrases does not matter.

 
       REPORT SECTION.
       RD  STOCK-SUMMARY
           LINE LIMIT 132
           FIRST DETAIL 5
           PAGE LIMIT 64

There are other clauses available to mark out different regions of the page.  (See PAGE LIMIT Clause.)

Our report has control totals.  That is, after the change in value of a certain control field (DEPOT), we want COBOL Report Writer to produce an extra report group (the CONTROL FOOTING).  The data must arrive in the correct sequence as COBOL Report Writer does not SORT your data itself.  (You might use COBOL SORT for that.)  The field is called a control and a change in its value is called a control break.  You may nest as many different levels of control as you need.  You may also have corresponding CONTROL HEADING groups to appear before the start of the detail lines for the new control value.  (In our example, there is just one level of control and no CONTROL HEADING group).  You indicate which fields are to be used to test for control breaks by means of the CONTROL (or CONTROLS) clause.  Each control represents a different level.  Your controls must be listed in hierarchical order from highest down to lowest.  In our example it is simple because there is only one level:

 
        REPORT SECTION.
        RD  STOCK-SUMMARY
            LINE LIMIT 132
            FIRST DETAIL 5
            PAGE LIMIT 64
            CONTROL IS DEPOT.

Because the CONTROL clause is the last clause of the RD entry, you write a period (".") after it.  Here is another example of a CONTROL clause.  This time, we have two control fields and also a special all-encompassing level, known as REPORT or FINAL, that may be used for producing grand totals for the whole report.

 
         CONTROLS ARE REPORT, YEAR, MONTH

LINE LIMIT, FIRST DETAIL, PAGE LIMIT, and CONTROL are not the only clauses you can write in the RD entry.  The others are described in the chapter Report Files and RD Entries.  The order in which you code the RD clauses is irrelevant.


Step 4:   Code the Report Group Descriptions

Step 4A:   01-Level Entries

Each report group is coded as a series of COBOL entries.  Each entry consists of a level-number, an optional data-name, any number of optional clauses, and a period.  Each report group must start with a 01 level-number in the A-margin:

 
        01

If the group is a DETAIL, follow this with a report-group data-name of your choice, followed by the optional word TYPE and the type of the group:

 
        01  TYPE PH.
            ... etc ...

        01  COOKIE-LINE  TYPE DE.
            ... etc ...

        01  TYPE CF.
            ... etc ...

To make your program even clearer, you may spell out the TYPE clause in full; for instance: TYPE IS PAGE HEADING.  Any number of entries, indicated by our "...", may follow the 01-level entry, as you will shortly see.

You indicate which level of CONTROL HEADING and CONTROL FOOTING you are describing by writing FOR name-of-control after CH and CF.  (This is optional if there is only one level, or you want ALL levels in a CONTROL FOOTING).  Taking our example with three levels above, you might code:

 
        01  TYPE CH FOR YEAR.
            ...
        01  TYPE CH FOR MONTH.
            ...



Step 4B:   LINEs and COLUMNs

After each group's 01-level entry, code a series of LINE entries, each containing a series of COLUMN entries.  COLUMN may be abbreviated as COL.  (You will see, in the chapter Report Group Descriptions, that you can also code a dummy group without LINEs or COLUMNs.)  You indicate that an entry is at a lower level by increasing the level-number.  For instance, you might choose 03 for LINE entries and 05 for COLUMN entries:

 
        01  TYPE PH.
          03  LINE ...
            05  COL ...

After the LINE or COL keyword you may choose one of two ways to specify positioning for your line or field by writing either integer or +integer.

 ¶   LINE integer gives you an absolute, that is, fixed, LINE position, counting from 1 at the top of the page, to the maximum given in your PAGE LIMIT.  If you use absolute LINE clauses in a group, the integers must increase.  You might use this form for the PAGE HEADING in our example.

 ¶   LINE + integer (+ can also be written PLUS) gives you a relative LINE position.  It will cause the vertical position to move down that number of lines.  For example, LINE + 1 means place on the next lineLINE + 2 means leave one blank line.  (This form is rather like WRITE... AFTER ADVANCING..., translated entirely into data terms, of course.)  You may mix absolute and relative LINE clauses in the same report group, provided that you begin with an absolute LINE.  LINE alone implies LINE + 1.

       CRUMBLY COOKIE COMPANY   ORDERS   PAGE 1

 
DATE     TYPE       QUANTITY     VALUE OF ORDER
 (LINE 1)

 
(LINE 3 or LINE + 2)


 ¶   COLUMN integer gives you an absolute, i.e. fixed, COLUMN position for the left-hand character of the field, counting character positions from 1 at the left, up to the maximum given in your LINE LIMIT.  You can also anchor the field at its CENTER column by writing COLUMN CENTER integer, and you can fix the field at its RIGHT column by writing COLUMN RIGHT integer.  If you use these absolute COLUMN clauses, the integers must increase within a LINE.

 ¶   COLUMN + integer (COLUMN can be shortened to COL and + can be written PLUS) gives you a relative COLUMN position.  It will cause the current horizontal position to move right that number of positions from the last column of the preceding field (from zero if this is the first field) to the first column of the current field.  For example, COL + 1 means place this field in the next column without a gap, and COL + 2 means leave one column blank.


 line
 nos.
 column numbers
 
1                  20 or + 3
   1



  
5
or + 4
  XXXXXXXXXXXXXXXXX..YYYYYYYY
 .
 .
 .
 
ZZZZZZ

COBOL Report Writer writes your report groups vertically down the page in the order in which they are GENERATEdDETAIL and CONTROL groups (HEADING or FOOTING), which are known as body groups, are first checked to see whether all their lines will fit on the current page.  If your report group begins with an absolute LINE, report writer will advance to a new page if that LINE number has been reached or passed.  If your report group begins with a relative LINE, report writer checks the size of the report group.  If there is no room, or if there is not enough room for the whole group, report writer will advance to a new page.

Advancing to a new page involves automatically generating your PAGE FOOTING, if you defined one, followed by your PAGE HEADING, if you defined one.  If a body group (CH, DE or CF) begins with a relative LINE, it is positioned on the FIRST DETAIL line, irrespective of the value in the LINE clause.  (If you did not code a FIRST DETAIL sub-clause, it is assumed to be the line immediately following our PAGE HEADING, or line 1 if there is no PAGE HEADING.)



Step 4C:   VALUEs and SOURCEs

To complete your Report Group Descriptions, you need to specify the contents of the fields.  The two most usual ways, which are sufficient for this example, are as follows:

If the contents of the field consist of fixed text, write:

 
     VALUE "literal",  or simply: "literal"

If the contents of the report field come from a field in your COBOL DATA DIVISION, write:

 
     PICTURE (or PIC) picture-symbols   SOURCE name-of-field

Your SOURCE field may be defined in any section of your DATA DIVISION, or it may be a special register such as LINE-COUNTER.  The SOURCE keyword may be omitted.  You may use subscripts and qualifiers, for example: SOURCE BACK-PAY IN MASTER-RECORD (4).  You may also use arithmetic expressions and the word ROUNDED , if needed; for example:

 
     PIC $(9)9.99   SOURCE (MONTHLY-PAY * 12) + YEARLY-BONUS ROUNDED

You must code a PICTURE clause with the SOURCE clause.  This specifies the format in which you would like the field displayed and is the same clause as in elementary COBOL. It can be abbreviated as PIC.  Here are two examples:

 
            PIC $(8)9.99   SOURCE MONTHLY-PAY
            PIC X(32)      SOURCE NAME-OF-STUDENT

The rules for storing the field work exactly as for the MOVE (or the COMPUTE) statement of elementary COBOL.  If your SOURCE refers to a CONTROL field, then you will obtain the value before the control break if report writer is currently processing CONTROL FOOTING groups.  This is the only case where you do not obtain the value contained in the field at that instant.

Your layout may require a page number.  This is held in a special register (a dedicated internal COBOL location) called PAGE-COUNTER.  This location is set up automatically by report writer.  There are also LINE-COUNTER and COLUMN-COUNTER special registers.

Suppose that the record that supplies data for the layout above is defined in a standard file as follows:

 
       FD  COOKIE-FILE             LABEL RECORDS STANDARD.
       01  COOKIE-RECORD.
           05  DEPOT               PIC X(10).
           05  ORDER-DATE          PIC 9(6).
           05  COOKIE-TYPE         PIC X(12).
           05  QTY-ORDERED         PIC 9(4) COMP.
           05  QTY-IN-STOCK        PIC 9(4) COMP.
           05  ORDER-VALUE         PIC S9(5)V99 COMP.

Now we are ready to complete the PAGE HEADING group and the first line of the DETAIL group for the layout above, using these new clauses.

Code sample (A):

 
        REPORT SECTION.
        RD  STOCK-SUMMARY
            LINE LIMIT 132
            FIRST DETAIL 5
            PAGE LIMIT 64
            CONTROL IS DEPOT.
        01  TYPE PH.
          03  LINE 1.
            05  COL 12   VALUE "CRUMBLY COOKIE COMPANY   ORDERS".
            05  COL 47   VALUE "PAGE".
            05  COL +2   PIC Z9    SOURCE PAGE-COUNTER.
          03  LINE 3.
            05  COL 7    VALUE
                "DATE     TYPE       QUANTITY     VALUE OF ORDER".
        01  COOKIE-LINE  TYPE DE.
          03  LINE + 2.
            05  COL 4    PIC 99/99/99   SOURCE ORDER-DATE.
            05  COL 16   PIC X(12)      SOURCE COOKIE-TYPE.
            05  COL 29   PIC ZZZ9       SOURCE QTY-ORDERED.
            05  COL 41   PIC $(5)9.99   SOURCE ORDER-VALUE.
          ...


Step 4D: Conditional Items

There is one item in our layout that we do not want to produce every time.  This is the message "OUT OF STOCK".  We deliberately allowed for it by including it in the "box" we drew round the typical DETAIL group.  It should only be produced if the condition

 
               QTY-ORDERED > QTY-IN-STOCK

is true.  To make any item depend on a condition's being true or false, use the clause:

 
               PRESENT WHEN condition

Report writer will then automatically test the condition when it is about to produce the item.  If the condition is false, the item is ignored.  If you put the clause on a LINE entry, it is the whole line that is ignored.  (You can in fact put it at any level.  When a group field is ignored, so are all the fields within the group.)  In the case here, we do want the whole line to be ignored if the condition is false, so the following would be a valid description for the second line for the DETAIL group:

Code sample (B):

 
         03  LINE  PRESENT WHEN QTY-ORDERED > QTY-IN-STOCK.
           05  COL 2    VALUE "**OUT OF STOCK".


Step 4E: Totalling

The sample layout tells you to produce a total in the CONTROL FOOTING group.  COBOL Report Writer allows you to produce totals from virtually any numeric fields, and you may do it in any TYPE of group.  To produce a total, follow these two simple steps:

1.   Put a data-name at the front of the entry you want totalled.

2.   In another entry, use the clause: SUM OF data-name instead of SOURCE or VALUE.

First, you must go back to our coding for the DETAIL group above and add a data-name to the entry for ORDER-VALUE.  For example, you could re-write the last entry as:

 
           05  REP-ORD-VAL COL 41   PIC $(5)9.99   SOURCE ORDER-VALUE.

where REP-ORD-VAL is a new data-name of your choice.  Now you can code the CONTROL FOOTING group for our layout:

Code sample (C):

 
        01  TYPE CF.
          03  LINE + 3.
            05  COL 16   VALUE "TOTALS: DEPOT".
            05  COL + 2  PIC X(10)      SOURCE DEPOT.
            05  COL 41   PIC $(5)9.99   SUM OF REP-ORD-VAL .
          03  LINE.
            05  COL 41   VALUE "=========".

You may have any number of CONTROLS in your program, and you may have a CONTROL HEADING as well as a CONTROL FOOTING report group for each control.



Step 5: Code the SELECT...ASSIGN and FD

COBOL Report Writer requires a standard COBOL file to which to write your output.  So, to begin with, you need a SELECT...ASSIGN clause and an FD .  The FD entry may contain any clauses that you would normally use for a report file, plus a new clause: REPORT IS name-of-report.  You should not need a record description to follow.  For our example, you might write:

Code sample (D):

 
        IDENTIFICATION DIVISION.
        ... (other paragraphs as normal) ...
        FILE-CONTROL.
            SELECT COOKIE-FILE ASSIGN TO DATAIN.
            SELECT STOCK-PRINT ASSIGN TO LIST01.
        DATA DIVISION.
        FILE SECTION.
        FD  COOKIE-FILE.
        ... (description as in 4C) ...
        FD  STOCK-PRINT
            ...
            REPORT IS STOCK-SUMMARY.
        WORKING-STORAGE SECTION.
        01  WS-EOF           PIC X   VALUE "N".


Step 6: Code the PROCEDURE DIVISION

COBOL Report Writer is entirely under the control of your program, but at a higher level than is the case with elementary COBOL.  This means that no action will be taken until your program executes a report writer statement.  There are three of these:

   INITIATE name-of-report

This statement initializes your report at the start of the whole process.  Your program must do this before it is allowed to execute any other report writer statements.  It does not open the file.  Name-of-report is the data-name you wrote right after the RD.

   GENERATE detail-name

This statement generates one instance of a DETAIL report group.  Detail-name is the data-name you used to name your DETAIL report group.  The GENERATE also performs all the other actions that might be necessary before the DETAIL report group is output, namely:

 ¶   It tests for control breaks (if your report has a CONTROL clause) and, if necessary, produces CONTROL FOOTING and CONTROL FOOTING report groups.

 ¶   It checks that your DETAIL report group will fit completely on the current page (assuming that your report has a PAGE clause).  If not, it produces your PAGE FOOTING report group (if there is one defined in your report), advances to a new page and produces your PAGE HEADING report group (if there is one defined in your report).  Unless you explicitly allow it with a MULTIPLE PAGE clause, report writer never splits your report group over two pages.

If it is the first occasion after the INITIATE, the GENERATE statement will output any REPORT HEADING, your PAGE HEADING, and all your CONTROL HEADING groups before generating your DETAIL report group.

Your CONTROL HEADING and CONTROL FOOTING report groups are also subject to the page-fit test.  They are treated similarly to DETAIL report groups.  These three TYPEs are often referred to as body groups, because they fit into the "body" of the page (between the PAGE HEADING and FOOTING) and usually contain the most important information.

   TERMINATE name-of-report

This statement ends your report and produces any final items that are required at the end of the report, namely:

 ¶   All CONTROL FOOTINGs up to the highest level defined;

 ¶   The last PAGE FOOTING (if defined);

 ¶   The REPORT FOOTING (if defined).


To produce output for a simple report layout from standard files, the following logical structure should apply:

 
           OPEN input and report files
           INITIATE report-name

   for each input record:

           READ input file
           GENERATE detail-name

   at end-of-file:

           TERMINATE report
           CLOSE all files

If your input is from a database, or reaches your program's DATA DIVISION by some means other than from a standard file, you will need to replace the OPEN and CLOSE for the input files and the READ by more appropriate statements.

To produce a more complex layout, you would probably define several different DETAIL report groups and decide in your program when to GENERATE one or the other.  For our example, the following would be a suitable complete PROCEDURE DIVISION:

Code sample (E):

 
        PROCEDURE DIVISION.
        PROGRAM-START.
            OPEN INPUT COOKIE-FILE, OUTPUT STOCK-PRINT
            INITIATE STOCK-SUMMARY
            PERFORM NEXT-RECORD
            PERFORM GENERATE-LINE THRU NEXT-RECORD
              UNTIL WS-EOF = "Y"
            TERMINATE STOCK-SUMMARY
            CLOSE STOCK-PRINT, COOKIE-FILE
            STOP RUN.
        GENERATE-LINE.
            GENERATE COOKIE-LINE.
        NEXT-RECORD.
            READ COOKIE-FILE AT END MOVE "Y" TO WS-EOF.

Place this code after the code in the code samples (D), (A), (B) and (C) (in that order) and you have a complete program.

You may use report writer "verbs" just as you would use any other PROCEDURE DIVISION statements.  So your program may do many other tasks apart from just producing your report output.




More about COBOL Report Writer



More about Files and Reports

You may describe as many reports as you like per program.  Each report has its own RD entry, followed by one or more Report Group Descriptions.  Separate reports may either be assigned to separate files or to the same file, in which case you could write:

 
       FD  PRINT-FILE
           REPORTS ARE MAIN-REPORT, SUMMARY-REPORT.

This last approach is useful where you need to produce a report that has distinct sections, perhaps with different page headings.  So a single physical report (as the end-user sees it) may consist of several different logical reports (as the programmer sees them), all written to the same file.

You can also direct your report output to a special  Independent Report File Handler, designed to process the output from reports in a particular way.  To use the special file handler, write the extra clause: MODE IS mnemonic-name in the SELECT clause.  It does not affect the FD or any other statements in your program.


More about the RD Entry



Apart from the clauses used in our example, there are several other clauses that you may write in your RD entry.  They are all explained in detail in the chapter Report Files and RD Entries.  Here is a brief summary:

LAST DETAIL gives the last line on which a DETAIL or CONTROL HEADING report group may appear.

LAST CF (or LAST CONTROL FOOTING or just FOOTING) gives the last line on which a CONTROL FOOTING group may appear.  If you do not specify it, a default value is assumed for it.  You can use it to ensure that a CONTROL FOOTING will never appear at the top of a page (since there will always be space reserved for it at the bottom of the previous page).

OVERFLOW and SUM OVERFLOW enable you to specify the action that takes place if any arithmetic expressions or totals defined in your report groups are too large for the report field or involve dividing by zero.

CODE is used when your output report data must have extra unprinted information placed at the start of each record, or when you need to pass information to a special Independent Report File Handler (see below).  For example, if your installation has provided a file handler to do spooling and restart, you may be required to provide a key by which restart would be done.  You would then write: CODE IS name-of-key-field.

ALLOW SOURCE SUM CORR and ALLOW NO SOURCE SUM CORR determine whether the ANS-68 or the ANS-85 rules will be used for calculating certain SUM fields.  ALLOW NO SOURCE SUM CORR is assumed in default in the version as supplied.

GLOBAL makes the report available to nested programs.



More about CONTROLS



Each RD entry may have a CONTROL clause, and you may write the names of any number of fields in your program.  Your control fields must have a hierarchy and must be listed in order from the highest down to the lowest.  When your program issues a GENERATE, report writer tests each control field in order, beginning with the highest.  If it detects a change (a control break), this process ends.  Report writer will then automatically take additional action before it produces the DETAIL group, depending on which CONTROL FOOTING or CONTROL HEADING report groups (if any) you defined.

COBOL Report Writer keeps an internal copy of each of your control fields so that it can test for a control break by comparing them with the new values on each GENERATE.  Before it produces your CONTROL FOOTING report groups, it temporarily stores these previous values back into the control fields.  So if your CONTROL FOOTING refers to a control field, as a SOURCE for example, you will get the previous or pre-break value, even though the original control field has changed in value.

You may also want a major heading and a major footing at the very start and end of your report.  For example, you may want to produce grand totals for the entire report.  If so, you may use the reserved word REPORT or FINAL.  This is the highest possible control level.  If you use it, it must therefore be first in the list of controls in your CONTROLS clause.

There may be fields other than totals or lines that should be produced only once after a control break.  These may be inside a report group, where you cannot make use of a separate CONTROL HEADING.  You can define them by writing:

 
             PRESENT AFTER NEW name-of-control-field
         or  ABSENT AFTER NEW name-of-control-field

The field or line will then appear only on the first occasion after a control break at the level you indicate.  (Alternatively, if you use ABSENT AFTER..., the field or line will not appear the first time and will appear every time thereafter until the next control break.)  You may also write PRESENT or ABSENT AFTER NEW PAGE, indicating that you want the field or line to appear (or disappear) only on the first occasion after a page advance.  Finally, you may code both the control-field and PAGE operands in one clause.

It is possible for a report to have no DETAIL groups at all.  This case is called summary reporting.  The only body groups coded are CONTROL FOOTING - and possibly CONTROL HEADING - groups.  Therefore, since you have no name of a DETAIL group to give in your GENERATE statement, you write: GENERATE report-name.

The next example illustrates all the points made in this section.  There are three levels of control, including REPORT, each with a CONTROL FOOTING.

  SPORTS CLUB: TENNIS SECTION
 
SUBSCRIPTIONS:  FULL     OFF-PEAK

 
1991 JAN        $4000      $10000
      FEB        $3000       $9000
      MAR        $2500      $11000
      ...         ...         ...
      DEC        $1500       $5500

 
1991 TOTALS:   $26500      $43000
                
======      ======

 1992 JAN        $2000       $8000
      FEB        $2500       $9500
      ...         ...         ...
      DEC        $2000       $4500
     S
 1992 TOTALS:   $34700      $12800
                ======      ======

GRAND TOTALS:   $61200      $55800

   RD  SUBSCRIPTIONS
       FIRST DE 4  PAGE LIMIT 60
       CONTROLS REPORT, YEAR, MONTH.
   
01  TYPE PH ... etc ...
   
01  TYPE CF FOR MONTH    LINE + 1.
       05  COL 1   PIC 9(4) SOURCE YEAR
           PRESENT AFTER NEW YEAR.
       05  COL 6   PIC XXX
           SOURCE W-MONTH-NAME (MONTH).
       05  COL 16  PIC $(5)9 SUM OF FULL.
       05  COL 28  PIC $(5)9 SUM OF OFFP.

   
01  TYPE CF FOR YEAR NEXT GROUP + 1.
     03  LINE + 2.
       05  COL 1   PIC 9(4) SOURCE YEAR.
       05  COL 6   VALUE "TOTALS:".
       05  COL 16  PIC $(5)9 SUM OF FULL.
       05  COL 28  PIC $(5)9 SUM OF OFFP.
     03  LINE + 1  COLS 16 28
         VALUE "======".
  *Blank line:
     03  LINE + 1.

   
01  TYPE CF FOR REPORT    LINE + 1.
       05  COL 16  PIC $(5)9 SUM OF FULL.
       05  COL 28  PIC $(5)9 SUM OF OFFP.

   PROCEDURE DIVISION.
       ...
       GENERATE SUBSCRIPTIONS

There is much more about CONTROLS in the main section (see CONTROL Clause).



More about TYPEs



In the diagram on the next page, you can see all seven types of report group in use in the same layout.  You may choose any or all of the seven types in a report, and none of them are compulsory (except that you must have at least one body group (CH, DE, or CF)).  You cannot have more than one REPORT HEADING, PAGE HEADING, PAGE FOOTING, and REPORT FOOTING, and you cannot have more than one CONTROL HEADING and CONTROL FOOTING for each control level.  But you can code any number of DETAIL groups.

If your report needs a particularly long or complex REPORT HEADING or REPORT FOOTING, or if your report layout changes completely at a later stage, code a second separate RD with its own Report Group Descriptions following and expand the REPORT clause of your FD to include the second report-name.  (You may associate as many RDs as you like with the same FD.)  For further details, see Report Files, REPORT SECTION and RD, and Multiple Reports.

  Report showing all 7 TYPEs of group:

 
   SPORTS CLUB EXPENSES SUMMARY
              1992
  TYPE RH or REPORT HEADING

 SPORTS CLUB EXPENSES      PAGE 1
  TYPE PH or PAGE HEADING
 SPORT: GOLF
 =====
  TYPE CH FOR SPORT
    or CONTROL HEADING FOR SPORT
    (higher control heading)   |
 JAN
 ---
  TYPE CH FOR MONTH
    or CONTROL HEADING FOR MONTH
    (lower control heading)    |
  01    MOLE DAMAGE         $350   TYPE DE or DETAIL
  23    NEW CAR PARK       $2250  (same)
 --------------           ------
 JAN GOLF TOTAL            $2600
 --------------           ------

  TYPE CF FOR MONTH
    or CONTROL FOOTING FOR MONTH
    (lower control footing)    |
 FEB
 ---
  ..    .....


 ---------                ------
 DEC TOTAL                 $2600
 ---------                ------


 YEAR GOLF TOTAL          $15000
 ===============          ------
  TYPE CF FOR SPORT
    or CONTROL FOOTING FOR SPORT
    (higher control footing)   |
 SPORT: CRICKET
 =====

 JAN
 ---

                  CONTINUED ...   TYPE PF or PAGE FOOTING


     END OF EXPENSES SUMMARY

  TYPE RF or REPORT FOOTING


Automatic Repetition



If your report has a series of fields or lines or groups of similar layout or format, it is usually possible to save time in coding by writing one multiple clause instead of several entries with single-operand clauses.  Here is a list of cases:


VALUES in Consecutive Fields

If you have consecutive fields in a line containing literals, you may code multiple COLUMNS and VALUE clauses to avoid writing several entries:

 SPORTS CLUB SUBSCRIPTIONS
 
TENNIS  GOLF  SWIMMING  CRICKET

      03  LINE + 1.
        05 
COLS 1 9 15 25
            
VALUES "TENNIS" "GOLF" "SWIMMING" "CRICKET" .

SOURCES in Consecutive Fields, with Same PICTURE

If you have several consecutive fields in a line with the same PICTURE (or if you can expand shorter PICTUREs to match longer ones), you may code multiple COLUMNS and SOURCE clauses in one entry:

 SPORTS CLUB SUBSCRIPTIONS
 TENNIS  GOLF  SWIMMING  CRICKET

  
$238   $340    $500      $350

        05  COLS 1 8 16 26  PIC $$$$9
            
SOURCES TENNIS GOLF SWIMMING CRICKET .

Here, as usual, each SOURCE field is a data item defined in the DATA DIVISION of your program (in this particular case, a numeric item).  VALUE and SOURCE clauses cannot be combined within the same multiple clause.

Using a single entry like this also makes it easy to total a series of fields by coding just one SUM entry:

 SPORTS CLUB SUBSCRIPTIONS
 TENNIS  GOLF  SWIMMING  CRICKET   TOTAL

  $238   $340    $500      $350   
$1428

       05  R-SUBS  COLS 1 8 16 26      PIC $$$$9
                   SOURCES TENNIS GOLF SWIMMING CRICKET.
       05  COL 35                      PIC $(5)9    
SUM OF R-SUBS.

If separate entries are used, you would have to total them by writing either: SOURCE TENNIS + GOLF + SWIMMING + CRICKET, or SUM R-TENNIS R-GOLF R-SWIMMING R-CRICKET, placing these data-names on the entries in turn.


Regularly Spaced COLUMNS

If the gap between successive fields is regular, you need not give a COLUMN for each one.  Instead, you can combine an OCCURS clause and a STEP phrase:

 SPORTS CLUB SUBSCRIPTIONS
 TENNIS  GOLF  RUGBY  SQUASH

  
$238   $340   $500   $350

      05  COL 1  OCCURS 4  STEP 7  PIC $$$$9
          SOURCES TENNIS GOLF RUGBY SQUASH.

You can also use OCCURS with a relative COLUMN to provide the gap, in which case the STEP phrase is unnecessary.

Repeating the Same VALUE

You can combine a single VALUE with an OCCURS clause or a multiple COLUMNS clause, in which case the VALUE is simply repeated:

   ($)     ($)     ($)     ($)
  238     340     500     350

       05  COL 2   OCCURS 4 STEP 8   VALUE "($)".
   (or 05 
COLS 2, 10, 18, 26  VALUE "($)".)

A single-operand SOURCE field can be similarly repeated, although the occasions for doing so are rarer.

Repeating LINE

The LINE clause also has a multiple form.  You may also combine an OCCURS clause with a single-operand LINE clause.  (In the latter case, if you use STEP , as you must if the LINE is absolute, it refers to the vertical distance.)  If you use a SOURCE, the entire table of SOURCE items must be "read into memory" first.  Within the repeating LINE, you may have multiple VALUES and SOURCES clauses.  This enables you to improve clarity by stacking your heading values in one place:

    NEW      OLD     MEMBERS
 
MEMBERS  MEMBERS   LEAVING

   03  LINES 2, 3.
     05  COL 1   VALUES
"  NEW"
                        
"MEMBERS".
     05  COL 10  VALUES
"  OLD"
                        
"MEMBERS".
     05  COL 19  VALUES
"MEMBERS"
                        
"LEAVING".

(You need not code the literals vertically like this, but it will aid the eye.)

Variable Number of Repetitions

If the number of repetitions is variable, you should use the OCCURS clause's keyword TO and DEPENDING ON phrase, whose operand can be any data-name or arithmetic expression.  Report writer will then dynamically calculate the actual number of repeats present on each occasion.  It is valid for there to be no occurrences, so your minimum can be zero.  Any "unused" repeats are treated as ABSENT:

 FAMILY MEMBERSHIPS                             AMOUNT DUE

 JONES    
PETER    MARY     IAN      SARAH         $240
 SMITH    
ALAN     DEBBIE                         $120
 ROBERTS  
SUSAN    TOM      IONA                   $180

         03  LINE.
           05  COL 1    PIC X(10)   SOURCE SURNAME.
           05  COL 11   PIC X(8)
               
OCCURS 2 TO 4 DEPENDING ON NO-MEMBERS-IN-FAMILY
               
STEP 9   VARYING FORENAME-SUB
               
SOURCE FORENAME (FORENAME-SUB) .
           05  COL 50   PIC $(4)9   SOURCE NO-MEMBERS-IN-FAMILY * SUBSCRIPTION.

The same method can be used for LINEs.  If a body group has a variable number of lines and they are all relative, report writer will take into account only those lines actually present when applying its page-fit test.

SOURCE Items in a Table

If you need to output SOURCE items that are held in a table, report writer will automatically vary an internal data-name which you can then use as a subscript.  You can specify a FROM value for the starting point and a BY value for the increment for your subscript, but these are assumed to be 1 if you omit them:

 SPORTS CLUB SUBSCRIPTIONS
 TENNIS  GOLF  RUGBY  SQUASH

 
$238   $340   $500   $350

      05  COL 1  OCCURS 4  STEP 7
          
VARYING SPORT-NO   PIC $$$$9
          
SOURCE SPORT (SPORT-NO).

You choose your own data-name for the VARYING clause, but it must not be defined anywhere as a data item in your program.  You can reuse the same data-name many times in the REPORT SECTION, except where the VARYING clauses are nested.

You may combine VARYING with a multiple COLUMNS or LINES clause, as well as with an OCCURS clause, and you may output results in more than one dimension.  In the next example, the SPORT fields are printed in reverse order:

  SPORTS CLUB 4-YEAR SUBSCRIPTIONS
        
SQUASH  RUGBY  GOLF  TENNIS

  1989   $180   $300   $445   $290
  1990   $196   $280   $440   $310
  1991   $223   $320   $450   $320
  1992   $238   $340   $500   $350

         03  LINE  OCCURS 4     VARYING YEAR-NO.
           05  COL 2  PIC 9(4)  SOURCE 1988 + YEAR-NO.
           05  COL 8  OCCURS 4  STEP 7  VARYING SPORT-NO FROM 4 BY -1
                      PIC $$$$9 SOURCE SPORT (YEAR-NO, SPORT-NO).


Repeating Whole Groups Horizontally

The REPEATED clause enables you to place whole groups side-by-side.  On each GENERATE, report writer will place the group in an internal buffer, until the last of each set arrives, whereupon the whole set will be printed side-by-side.  You should define only the left-hand group.

                     CRICKET FIXTURES
 +-------------------------+   +-------------------------+
 |  1ST TEAM VS OLD C.T.'S |   |  2ND TEAM VS S.RICHMOND |
 |  ON 21ST APRIL          |   |  ON 21ST APRIL          |
 |  AWAY                   |   |  HOME                   |
 +-------------------------+   +-------------------------+
 +-------------------------+   +-------------------------+
 |  1ST TEAM VS OLD C.T.'S |   |  2ND TEAM VS S.RICHMOND |
 |  ON 28TH APRIL          |   |  ON 28TH APRIL          |
 |  HOME                   |   |  AWAY                   |
 +-------------------------+   +-------------------------+
  . . . . . . . . . . . . . . . . . . . . . . . . . . . .

       01  CRICKET-FIXTURE   TYPE DE   REPEATED 2 TIMES EVERY 30 COLS.
         03  LINE + 3.
           05  COL 4     VALUE "1ST"   WHEN REPEATED-COUNTER = 1
                         VALUE "2ND"   WHEN OTHER.
           05  COL 8     VALUE "TEAM VS".
           05  COL + 2   PIC X(10)     SOURCE OPPONENTS-NAME.
         03  LINE + 1 ... etc ...
         03  LINE + 1 ... etc ...

If a different DETAIL group is GENERATEd - say SOCCER-FIXTURE - or if your program issues a TERMINATE, and there are still left-hand groups in the buffer, these buffered groups are output first, padded out with blank entries on the right where necessary.

Different Levels Using the same CONTROL FOOTING

You will have noticed from some of the preceding examples that a lower CONTROL FOOTING and a higher CONTROL FOOTING often have a very similar layout and you may wish you could code a single report group and use it for any number of control levels.  You can do this simply by listing more than one control in the TYPE clause, for example TYPE CF FOR REPORT, YEAR, MONTH or just CF FOR ALL.  Any SUM totals are then automatically rolled forward up to each higher level.  If any CONTROL FOOTING has a different layout from the others, you can use PRESENT WHEN CONTROL IS YEAR, PRESENT WHEN CONTROL IS MONTH, and so on to vary it.



More about Totalling



There are many other ways to use the SUM clause to produce totals.  As well as totalling from one group to another, you may form totals within the same group .  Here's how you might enhance our four-yearly table with row and column totals.  (Absorb this example slowly.)

 SPORTS CLUB 4-YEAR SUBSCRIPTIONS
        SQUASH  RUGBY  GOLF  TENNIS   TOTAL

  1989   $180   $300   $445   $290   
$1215
  1990   $196   $280   $440   $310   
$1226
  1991   $223   $320   $450   $320   
$1313
  1992   $238   $340   $500   $350   
$1428
        -----  -----  -----  -----    -----

 TOTALS 
$837  $1240  $1835  $1270    $5182

         03  LINE  OCCURS 4      VARYING YEAR-NO.
           05  COL 2  PIC 9(4)   SOURCE 1988 + YEAR-NO.
           05  R-VAL  COL 8  OCCURS 4   STEP 7
               VARYING SPORT-NO FROM 4 BY -1
               PIC $$$$9  SOURCE SPORT (YEAR-NO SPORT-NO).
           
05  COL 37 PIC $(5)9  SUM OF R-VAL .
         03  LINE     COLS 8 15 22 29 37   VALUE "-----"
         03  LINE.
           05  COL 1  VALUE "TOTALS".
           
05  T-VAL  COL 8  OCCURS 4   STEP 7   PIC $$$$9  SUM OF R-VAL.
           
05  COL 37 PIC $(5)9  SUM OF T-VAL .
      *Totals may also be specified at the top or on the left!


Report writer totals repeating values automatically along the horizontal or vertical axes.  Notice that you should not place any subscripts after the data-name that is the operand of the SUM clause.

SUM may be combined in an entry with a multiple COLUMN (or LINE) clause to give you a series of totals of another repeating entry with the same number of repetitions, as you see in the last line of this example:

           CLUB OUTGOINGS IN 1992
  MONTH  BUILDINGS  INTERIOR  WAGES     TAX

   JAN      
$80       $445    $2290    $121
   FEB      $170       $350     $440   $2260
   ...       ...        ...      ...     ...
   DEC      $190       $440    $2260   $1130
           -----      -----    -----   -----
   TOTALS 
$3120      $1240   $13250  $34930

         03  LINE          OCCURS 12  VARYING MONTH.
           05  COL 2       PIC XXX    SOURCE WS-MONTH-NAME (MONTH).
           05 
R-OUTGOINGS COLS 10 20 29 37  PIC $(5)9
                           
SOURCES BUILDINGS INTERIOR WAGES TAX.
         ...
         03  LINE.
           05  COL 2       VALUE "TOTALS".
           05 
COLS 9 19 28 36  PIC $(6)9    SUM OF R-OUTGOINGS.

The total line may also be in a different group from the repeating line.  If so, you might then remove the OCCURS 12 on the first LINE entry and GENERATE the group containing it 12 times.

As well as totalling a field using SUM, you may count the occurrences using the COUNT clause.  COUNT simply adds 1 each time instead of the value of the field.  You may COUNT the number of times any REPORT SECTION item appears, including LINEs or whole groups.  All multiple occurrences contribute to the COUNT.

You may use SUM and COUNT as terms of a SOURCE expression.  Be sure to enclose each term in parentheses.  For example, to find the average amount of the subscription of our four sports above, you may write:

 
       01  MAIN-GROUP  TYPE DE.
           05  R-SUBS  COL 1       PIC $$$$9   SOURCE SPORT-SUBSCRIPTION.
           ...
       01  TYPE CF.
           05  COL 1   PIC $$$$9
               SOURCE IS  (SUM OF R-SUBS) / (COUNT OF R-SUBS)  ROUNDED.

(As usual, the words SOURCE IS are optional.)  If the divisor (the COUNT term above) happens to be zero, report writer will detect the error, unless you write OVERFLOW PROCEDURE IS OMITTED in your RD statement.  The action taken depends on what, if anything, you coded in the OVERFLOW PROCEDURE clause.  (By default, report writer will detect the error and write an error message on your terminal or job log, leaving the field blank.)

You may also total numeric fields directly from other sections in your DATA DIVISION.  (With the older ANS COBOL report writer this method was necessary to obtain totals.  You coded the name of the FILE, WORKING-STORAGE, or LINKAGE SECTION item as an operand of the SUM clause in the lowest-level CONTROL FOOTING.)  With such external items, you may use subscripts, and you may also SUM an arithmetic expression; for instance:

 
         05  COL 1   PIC ZZZ9   SUM OF (WS-INCOME - WS-TAX).

If the item does not already appear as a SOURCE, this is the only method of totalling it.  So this technique is useful where you require totals of a field but do not want to show the individual values that were added to produce the total.  Its main disadvantage is that it may not be clear to the reader of your program exactly when the values are added into the total.  See the remainder of this publication for a discussion of the relevant rules.



More about Conditional Entries



You have already seen how a single COBOL condition may be used to decide whether to output a report field.  Multiple-choice entries are used when you have several possible contents for a field.  Just write a series of SOURCE or VALUE clauses, each followed by PRESENT WHEN condition.  (The keyword PRESENT is often omitted in a multiple-choice entry.)  The period does not come until the end.  Report writer examines all the conditions in sequence until it finds the first that is true and then uses the VALUE or SOURCE associated with that true condition.  WHEN OTHER can be used to indicate "when none of the given conditions is true".  (Compare the use of ELSE in elementary COBOL.)  Study the following example:

                    NEW MEMBERS
 TITLE/NAME         PAY MONTHLY       AMOUNT DUE
                    OR YEARLY?
 MR. CODER              M                
$10
 MISS PROGRAMMER        Y               
$120
 ANALYST                Y               
$160

         03  LINE.
           05  COL 1    VALUE "MR. "   WHEN TITL = 1
                        VALUE "MRS. "  WHEN TITL = 2
                        VALUE "MISS "  WHEN TITL = 3
                        VALUE "DR. "   WHEN TITL = 4 .
      *NB: One period after last item!
           05  COL +1   PIC X(12)      SOURCE SURNAME.
           05  COL 24   VALUE "M"      WHEN YEARLY-FLAG = 0
                        VALUE "Y"      WHEN OTHER.
           05 
COL 40   PIC $$$$9
                        
SOURCE (SUBSCRIPTION / 12) WHEN YEARLY-FLAG = 0
                        
SOURCE SUBSCRIPTION  WHEN OTHER.

Note that the third person in our list, ANALYST, has no title because there is no WHEN OTHER ("catch-all") in the choice of titles.

You may produce many useful effects with the PRESENT WHEN clause by causing fields or lines, relative or absolute, to appear or disappear at certain times.  If a relative entry (COLUMN + ... or LINE + ...) follows an entry that may or may not be PRESENT, its position is variable:

           UNPAID SUBSCRIPTIONS
 TESTER     ( CRICKET SQUASH ): $250
 CODER      ( RUGBY ): $100
 ANALYST    ( TENNIS SQUASH RUGBY ): $450
 . . . . . . . . . . . . . . . . . . . . . .

         03  LINE.
           05  COL 1     PIC X(10)   SOURCE SURNAME.
           05  COL 12    VALUE "( ".
           05  COL +1    VALUE "CRICKET " PRESENT WHEN CRICKET-FLAG = 1.
           05  COL +1    VALUE "TENNIS "  PRESENT WHEN TENNIS-FLAG = 1.
           05  COL +1    VALUE "SQUASH "  PRESENT WHEN SQUASH-FLAG = 1.
           05  COL +1    VALUE "RUGBY "   PRESENT WHEN RUGBY-FLAG = 1.
           05  COL +1    VALUE "):".
           05  COL +2    PIC $$$9    SOURCE UNPAID-SUBS.

The ABSENT WHEN clause has the same effect as PRESENT WHEN except that you write the negative condition.  Other conditional clauses are PRESENT AFTER (previously known as GROUP INDICATE) and ABSENT AFTER.  Instead of checking a standard COBOL condition, these clauses test whether there has been a page advance or a control break since the group was last produced.  You may write PRESENT AFTER NEW PAGE, PRESENT AFTER NEW control-id, or PRESENT AFTER NEW control-id OR PAGE :

             SURVEY OF MEMBERSHIP
  YEAR  MONTH   GOLF  RUGBY  TENNIS  SQUASH

  
1991   JAN     350    100    500    250
         FEB     360    120    450    260
         ...     ...    ...    ...    ...
         DEC     340    125    250    360
  
1992   JAN     360    105    400    150
         FEB     260    150    250    260
             SURVEY OF MEMBERSHIP
  YEAR  MONTH   GOLF  RUGBY  TENNIS  SQUASH

  
1992   MAR     250    130    400    350
         APR     380    100    650    190
         ...     ...    ...    ...    ...

       RD  MEMBERS-SURVEY
           PAGE LIMIT 60   LINE LIMIT 132
           CONTROL IS YEAR-NO.
       01  SURVEY-FIGURES  TYPE DE    LINE + 1.
           05 
COL 1       PIC 9(4)   SOURCE YEAR-NO
               
PRESENT AFTER NEW YEAR-NO OR PAGE .
      *without the PRESENT AFTER clause, the YEAR would appear on each line.




Some Shorter Forms



COBOL Report Writer offers you several ways to shorten the amount of code you write.  You have already seen several, such as shortening COLUMN to COL .  Of course, the shorter forms may not always be clearer, and you may decide not to adopt them all.  Here are some of them:

The keywords TYPE, SOURCE, VALUE, and PRESENT may be omitted.  This reduces your coding effort at a cost of making your program less readable to a maintenance programmer unfamiliar with report writer.

If you do not code a TYPE clause in a level-01 entry, TYPE DETAIL is implied.

You may write LINE and COLUMN (or COL) in the same entry, provided that there is only one item in the LINE.  So you could code:

 
         03  LINE + 1   COL 20    VALUE "GOLF".

   instead of:

 
         03  LINE + 1.
           05  COL 20   VALUE "GOLF".

    If there is second item in the line, this second method is the only way.


You may code the LINE clause in the level-01 entry, provided that there is only one LINE in the report group.  So you could code:

 
       01  ACCOUNT-ENTRY    TYPE DE    LINE + 1.

   instead of:

 
       01  ACCOUNT-ENTRY    TYPE DE.
           05  LINE + 1.

If there is another LINE in the report group, this second method is the only way.




Other Features



Variable-Length Fields


If any of your report fields are to take up a variable number of columns, use the left-shift (or "squeeze") symbols "<" and ">" in the PICTURE.  The examples below show the effect of these symbols:

               MEMBERS AND CHILDREN'S AGES

 CODER: MANDY(7), TOM(5).
 TESTER: ALAN(11), HILARY(9), JASON(8).
 ANALYST: ANGELO(8).
 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

         03  LINE.
           05  COL 1        PIC <X(12)>  SOURCE SURNAME.
           05  COL + 1      VALUE ": ".
           05  OCCURS 1 TO 9 DEPENDING ON NUMBER-OF-CHILDREN
               VARYING R-CHILD-SUB.
             07  COL + 1    PIC <X(8)>   SOURCE FORENAME (R-CHILD-SUB).
             07  COL + 1    VALUE "(".
             07  COL + 1    PIC <9>9     SOURCE AGE (R-CHILD-SUB).
             07  COL + 1    VALUE ")".
             07  COL + 1    VALUE ", "   WHEN R-CHILD-SUB < NUMBER-OF-CHILDREN
                            VALUE "."    WHEN OTHER.

The reason why PIC <9>9 was coded rather than PIC <99 against the child's age is to prevent a value of zero from causing the field to vanish completely.  In the other cases, the closing ">" symbol is optional.

Now imagine this same code with all the "<" and ">" symbols removed from the PICTUREs.  This is what would appear:

               MEMBERS AND CHILDREN'S AGES

 CODER       : MANDY   (07), TOM     (05).
 TESTER      : ALAN    (11), HILARY  (09), JASON   (8).
 ANALYST     : ANGELO  (08).
 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


Insertion Characters



As well as by using standard PICTURE symbols such as "/", "0" and "B", you can place any additional characters into your report field by placing them within "quotes" (or 'apostrophes' ) within the PICTURE.  For example, to print a percentage:

 
             PIC ZZZ9.99"%" SOURCE 100 * COST / TOTAL ROUNDED


COLUMN CENTER and RIGHT



You can specify the center or the right-hand column as an anchor point, rather than just the left-hand column.  To do so, write COLUMN CENTER or COLUMN RIGHT.  (CENTRE is an alternative spelling.)  In the case of COLUMN CENTER, if your field has an even number of characters, the odd character goes on the right.  This feature saves you time when you are working with fields of different lengths, in different lines, that should appear centered or right-aligned in a "stack".  It also simply saves you the effort of counting out the length of a field in order to center it.  See the following cases, all of which produce the same result:

               Expenditure
               ^    ^    ^
  
COL 15 - - - +    |    + - - - COL RIGHT 25
             
COL CENTER 20

If your field is variable-length, report writer first takes the actual size of the field before it positions it.  In this way a name, title, etc. can be centered or right-aligned:

                JOHN CODER
            12 WALLINGTON ROAD
                  EGHAM

      03  LINE OCCURS 1 TO 5 DEPENDING ON NO-OF-ADDR-LINES
               VARYING R-ADDR-LINE.
        05  COL CENTER 20   PIC <X(32)  SOURCE ADDR-LINE (R-ADDR-LINE).


NEXT GROUP Clause



Use this clause when you want to create extra space between report groups or when you need to ensure that a particular report group is the last on the page, perhaps the CONTROL FOOTING of a major control.  With report writer, this clause is necessary only with body groups.  It has the useful property that, if there is a higher-level control break, the lower-level CONTROL FOOTING group does not affect the higher-level one, so that, if there is room, they normally remain together on the same page.

Write the clause in your 01-level entry for the group.  The form NEXT GROUP + integer will create integer extra blank lines following the group, provided it is not the last on the page.  The form NEXT GROUP NEXT PAGE causes your group to be the last on its page.



GROUP LIMIT Clause



You may not want some particular report groups to appear below a certain line on the page.  For example, a CONTROL HEADING would seem out of place if it were last on the page.  Just code GROUP LIMIT IS integer in the 01-level entry of your group.  Integer will then be the bottom line number allowed for the last line of the group.  See immediately below for an example.



CONTROL HEADING at Top of Every Page



Many report layouts have CONTROL HEADING groups that have to appear at the top of each page as well as after a control break.  If this is required, just write the words OR PAGE after the control-name in the TYPE clause of your CH group.  The following diagram shows this effect, and also illustrates the GROUP LIMIT clause that we discussed above.

          CLUB EXPENDITURE 1992

  
SPORT: GOLF
  
=====
  21 MAR   BUNKERS RESURFACED     $1500
  04 AUG   COFFEE ROOM TABLES      $260
  .. ...    ....
  12 DEC   XMAS DECORATIONS        $500

  
SPORT: RUGBY
  
=====
  03 JAN   REPAIR GOALPOSTS        $500
  11 FEB   BARSTOOLS                $80

Because of the GROUP LIMIT, the CONTROL HEADING will not appear after line 57.

          CLUB EXPENDITURE 1992

  
SPORT: RUGBY   (CONT.)
  
=====
  22 APR   REPAIR SHOWERS          $390

A CONTROL HEADING re-appears because of the new page even though no control break occurred.

 
       RD  CLUB-EXPENDITURE
           PAGE LIMIT 60   FIRST BODY GROUP 3   LINE LIMIT 132
           CONTROL IS SPORT.
       ...
       01 
TYPE CH FOR SPORT OR PAGE
           
GROUP LIMIT 58.
         03  LINE + 2.
           05  COL 1         VALUE "SPORT:".
           05  COL + 2       PIC X(8)  SOURCE SPORT.
           05  COL + 2       VALUE "(CONT.)"  ABSENT AFTER NEW SPORT.
         03  LINE            VALUE "=====".


MULTIPLE PAGE Groups



If you have a large vertical table to print, perhaps a summary with one line for each value encountered, you may be concerned that it will not always fit on one page.  Perhaps there are usually less than 60 items but you have to allow for anything up to 1000 items!  To handle this, code the clause MULTIPLE PAGE on your 01-level.  Report writer will then automatically do a page advance whenever the page is full (printing PAGE FOOTING and PAGE HEADING as usual).  Thus your code would be:

 
       01  SUMMARY-PAGES  TYPE DETAIL  MULTIPLE PAGE.
         03  LINE  OCCURS 0 TO 1000 DEPENDING ON NO-OF-ITEMS.
           ... etc.

This feature also handles more complex layouts, perhaps a multi-page personnel profile.



Line WRAP



You may sometimes define a number of relative COLUMN entries in one line and wonder whether they will all fit in the same line.  If not, report writer will automatically wrap your data round onto a continuation line, but only if you code a WRAP clause.  You can specify the last column before the wrap, the starting column for the continuation and the line advance required.  As an example, you may have a series of possible error messages:

 
         03  LINE + 3  WITH WRAP AFTER COL 120 TO COL 82 STEP 2.
           05  COL 1       PIC X(80)    SOURCE INPUT-RECORD.
           05  COL + 2     "ACCOUNT NUMBER INVALID"  PRESENT WHEN ...
           05  COL + 2     "AMOUNT NOT NUMERIC"      PRESENT WHEN ...
           05  COL + 2     "DATES IN REVERSE ORDER"  PRESENT WHEN ... etc.


FUNCTIONs



The FUNCTION clause is used when you need to produce a specially formatted or converted report field that cannot be produced by SOURCE, SUM, or VALUE .  Each FUNCTION corresponds to a pre-written routine that is either a built-in part of the report writer software or written by a person at your location.  Examples of built-in FUNCTIONs are:


DATE   This outputs today's date, or any given date, in the order:  Day-Month-Year.

MDATE   This produces the same output as DATE, but in the order:  Month-Day-Year.

TIME   This gives the current time.

Here is an example of how to use MDATE.  Let's suppose that today is May 7th, 1992.  Then if you write:

         PIC 99/99/99 FUNCTION MDATE
you will obtain
05/07/92
and if you write:
         PIC <X(9)B<99,B9(4) FUNCTION MDATE
you will obtain
MAY 7, 1992

Information about developing your own functions will be found later (see Developing User-Written Functions).



Special Print Attributes (Styles)



Nowadays all large system printers and smaller-scale printers and terminals have the ability to produce special effects which we hardly ever make any use of in COBOL applications.  The STYLE clause enables you to make full use of any special effects that are available without affecting your program's portability.  Supposing that you wish to highlight any "negative profits".  Write:

 

          05  COL 21   PIC -(8)9  SOURCE PROFIT
              STYLE IS HIGHLIGHT WHEN PROFIT < 0.

You will now not need to change your program when moving between, say, a personal system, a mainframe with a laser printer, and a mainframe with an old impact printer, except possibly to change the TYPE clause in the SELECT...ASSIGN if it is not preset as the default.  Also, the STYLE clause has no effect on the COLUMN clauses or any other part of your source program.

For more advanced information on creating and using styles, see Printer Styles.



Independent Report File Handlers



Normally, your report's outputs are directed to a standard print file, as though you had written the program in elementary COBOL using WRITE AFTER ADVANCING... statements.  An Independent Report File Handler is a pre-written routine to which all the output for a report file is directed.  It may manipulate and output the data in any way the designer chooses.  Your program can be made to invoke the file handler each time it has a line of report data, instead of implicitly executing a WRITE...AFTER ADVANCING.  Each file handler is identified by a unique "mnemonic-name" of up to four characters.  You cause your report program to use a file handler by coding your SELECT clause for the report file in the following way:

 

      SELECT print-file ASSIGN TO assignment-name
         MODE IS mnemonic-name.

The file handler may require you to define a CODE clause in your RD statement.  This clause is used to pass additional information to the file handler.  Apart from this, no other change need be made to your program.



Multiple Reports



Your program may need to produce several different physical reports.  Of course, you may define as many report files as you like, and each may be associated with as many Report Descriptions as you wish.  But what if several of the reports have a similar appearance?  You will not want to duplicate the code for all the Report Group Descriptions.  Instead, you may define the report just once and effectively assign it to several files (although only one FD entry is coded).  Just add the following clause to your SELECT clause:

 

      DUPLICATED integer TIMES

with the integer set to the maximum number of distinct reports you need.

The DUPLICATED clause causes the special register REPORT-NUMBER to be set up.  You can MOVE any value into REPORT-NUMBER from 1 to your maximum number.  This causes report writer to channel subsequent output to the corresponding report file.  Each report is logically separate.  Of course, the contents of each report are different because your program is writing to only one of the set at any given time.  The layouts need not all be identical, since you are quite free to vary them conditionally in the usual way.  (For example, REPORT-NUMBER could be used as a subscript or within the condition of a PRESENT WHEN clause.)

Only one FD entry is required for all the physical files associated with the multiple report.  Similarly, only one OPEN and one CLOSE are required to open and close all its files.  More details will be found later (see Multiple Reports).



Using the Page Buffer



Some layouts are so irregular that you may wish that you could build up the page in any order like a news-sheet editor.  The Page Buffer facility enables you to do this.  Just add to your SELECT clause:

 

         WITH PAGE BUFFER

Now you can tackle a layout such as the following:

            NEW MEMBER DETAILS
  NAME AND ADDRESS           SPORTS PLAYED

  
ANDREW ANALYST                TENNIS
  
21 MITCHAM ROAD               SQUASH
  
PUTNEY                        SWIMMING
  
LONDON SW6

       01  NAME-ADDRESS-GROUP  TYPE DE.
           ...
       01 
SPORTS-GROUP  TYPE DE.
           ...

The report groups in boxes have been defined separately.  Normally you would not be able to place them alongside each other.  (The REPEATED clause is not appropriate as NAME-ADDRESS-GROUP and SPORTS-GROUP are instances of different groups, not instances of the same group.)  By using the Page Buffer you may now write in the PROCEDURE DIVISION of your program:

 

               SET PAGE STATUS TO HOLD
               GENERATE NAME-ADDRESS-GROUP
               SET LINE TO FIRST DETAIL
               SET PAGE STATUS TO RELEASE
               GENERATE SPORTS-GROUP

You may store the groups on the page in any order.  It is also possible to change the left/right positioning of groups by means of the SET COLUMN statement.  There are several other variants of SET PAGE and SET LINE (see Report Writer SET Statements).