27 min read

> "The first time I saw a Report Writer program replace 400 lines of manual report code with 60 lines of declarations, I understood what COBOL was really designed for — describing business documents, not managing line counters." — Priya Kapoor...

Chapter 16: Report Writer

"The first time I saw a Report Writer program replace 400 lines of manual report code with 60 lines of declarations, I understood what COBOL was really designed for — describing business documents, not managing line counters." — Priya Kapoor, reflecting on her early mainframe career

16.1 Introduction: The Report Generation Problem

Report generation is one of the most common tasks in enterprise COBOL programming. Every bank, insurance company, hospital, and government agency produces reports — daily transaction summaries, monthly statements, quarterly regulatory filings, annual audits. These reports share common requirements:

  1. Page layout: Headers, footers, page numbers, date stamps
  2. Control breaks: Subtotals when a key field changes (new department, new account, new region)
  3. Accumulation: Running totals, subtotals, grand totals
  4. Formatting: Aligned columns, numeric editing, conditional text

Writing report programs manually means managing all of these concerns yourself: counting lines to detect page overflow, maintaining running totals, detecting control breaks, formatting header and footer lines. A typical manual report program might spend 60-70% of its code on report mechanics rather than business logic.

The Report Writer facility offers a declarative alternative. Instead of writing procedural code to manage every aspect of the report, you describe the report's structure in the DATA DIVISION — what the page looks like, where control breaks occur, what gets totaled — and then let the COBOL runtime handle the mechanics. You tell COBOL what you want the report to contain; it figures out how to produce it.

💡 Historical Note: Report Writer was part of the original COBOL specification in 1960. It was considered so important that it received its own module in the COBOL standard. While its usage declined in some shops during the 1980s and 1990s as ad-hoc reporting tools (like SAS, Crystal Reports, and later SQL-based tools) gained popularity, Report Writer remains valuable for batch reports that must be generated within COBOL batch programs — particularly when report generation is part of a larger processing pipeline.

16.1.1 The Theme: Readability is a Feature

This chapter's theme — Readability is a Feature — is particularly apt for Report Writer. A Report Writer program describes its reports declaratively: "this is the page heading, these are the detail lines, this is what happens at a control break." A reader can understand the report's structure by reading the data declarations alone. Compare this with a manual report program where the report structure is buried in procedural logic — IF statements checking line counts, PERFORM loops accumulating totals, paragraph after paragraph of MOVE and WRITE statements.

Priya Kapoor puts it this way: "When a new developer needs to modify a report, do you want them reading 400 lines of procedural code to understand the layout, or 60 lines of declarations that literally describe it?"


16.2 Report Writer Architecture

Report Writer involves several DATA DIVISION entries and three PROCEDURE DIVISION statements. Let us map out the architecture before diving into details.

16.2.1 The Report File (FD)

A report is written to a file, just like any other output. You define the file with a standard FD entry, but you add the REPORT IS clause:

       FD  REPORT-FILE
           REPORT IS DAILY-TRANSACTION-REPORT.
       01  REPORT-LINE          PIC X(132).

The REPORT IS clause links this file to a report description (RD) in the REPORT SECTION.

16.2.2 The Report Description (RD)

The RD entry describes the report's physical page layout:

       REPORT SECTION.
       RD  DAILY-TRANSACTION-REPORT
           CONTROLS ARE FINAL
                       ACCT-NUMBER
           PAGE LIMIT IS 60 LINES
           HEADING 1
           FIRST DETAIL 5
           LAST DETAIL 55
           FOOTING 58.

This declares: - CONTROLS: The fields that trigger control breaks. FINAL means a grand total break at the end of the report. ACCT-NUMBER is a minor control field. - PAGE LIMIT: The total number of print lines per page - HEADING: The first line where headings can print - FIRST DETAIL: The first line where detail records can print - LAST DETAIL: The last line where detail records can print (triggers page overflow) - FOOTING: The last line where footings (including control footings) can print

16.2.3 Report Groups

Within the RD, you define report groups — the building blocks of the report. Each group corresponds to a type of report line:

Report Group Type Purpose When Generated
REPORT HEADING (RH) First page of report — title, generation date Once at INITIATE
PAGE HEADING (PH) Top of each page — column headers Each new page
CONTROL HEADING (CH) Start of a new control group When control field changes
DETAIL (DE) Individual data lines Each GENERATE DETAIL
CONTROL FOOTING (CF) End of a control group — subtotals When control field changes
PAGE FOOTING (PF) Bottom of each page — page numbers Each page end
REPORT FOOTING (RF) Last page of report — grand totals At TERMINATE

16.2.4 The Three PROCEDURE DIVISION Statements

Report Writer uses just three statements:

  1. INITIATE report-name: Starts the report. Initializes all counters and SUM fields. Must be issued after the report file is OPENed.

  2. GENERATE {detail-group | report-name}: Produces report output. GENERATE detail-group writes a detail line (and triggers any necessary control breaks, page breaks, headings, and footings). GENERATE report-name produces a summary report with no detail lines.

  3. TERMINATE report-name: Ends the report. Produces final control footings and the report footing. Must be issued before the report file is CLOSEd.

📊 The Power of GENERATE: A single GENERATE statement can trigger a cascade of automatic actions: detect a control break, produce a control footing with subtotals, produce a control heading for the new group, check for page overflow, produce a page footing, advance to a new page, produce a page heading, and finally write the detail line. All of this happens declaratively — you do not code any of it.


16.3 Report Writer Syntax in Detail

16.3.1 The RD Entry

       RD  report-name
           [CONTROLS ARE {FINAL [data-name-1 ...] | data-name-1 ...}]
           [PAGE LIMIT IS integer-1 LINES]
           [HEADING integer-2]
           [FIRST DETAIL integer-3]
           [LAST DETAIL integer-4]
           [FOOTING integer-5].

CONTROLS defines the control break hierarchy. Fields are listed from major (leftmost) to minor (rightmost). FINAL is the most major — it triggers only at the end of the report. Example:

           CONTROLS ARE FINAL
                       REGION-CODE
                       BRANCH-CODE
                       ACCOUNT-NUMBER

This creates a three-level control break hierarchy: when ACCOUNT-NUMBER changes, account subtotals print; when BRANCH-CODE changes, branch subtotals print (and account subtotals for the last account); when REGION-CODE changes, region subtotals print (and branch and account subtotals cascade). FINAL triggers at report end for grand totals.

⚠️ Critical Rule: The control fields must be listed in major-to-minor order and must correspond to the sort order of the input data. If your data is sorted by region, then branch, then account, your CONTROLS must list them in that same order. Mismatched control/sort order produces incorrect subtotals.

PAGE LIMIT values define the print area:

Line 1  ─────────── HEADING (page headings start here)
Line 2
Line 3
Line 4
Line 5  ─────────── FIRST DETAIL (detail lines start here)
...
Line 55 ─────────── LAST DETAIL (last line for detail/CH)
Line 56
Line 57
Line 58 ─────────── FOOTING (last line for CF/PF)
Line 59
Line 60 ─────────── PAGE LIMIT (physical page boundary)

16.3.2 Report Group Definitions

Each report group is defined as a 01-level entry within the RD. The TYPE clause identifies the group type:

       01  TYPE IS PAGE HEADING.
           05  LINE 1.
               10  COLUMN 1    PIC X(40)
                   VALUE 'GLOBALBANK DAILY TRANSACTION REPORT'.
               10  COLUMN 50   PIC X(06)
                   VALUE 'DATE: '.
               10  COLUMN 56   PIC 99/99/9999
                   SOURCE IS WS-REPORT-DATE.
           05  LINE 2.
               10  COLUMN 1    PIC X(132)
                   VALUE ALL '-'.
           05  LINE 3.
               10  COLUMN 1    PIC X(10)
                   VALUE 'ACCOUNT'.
               10  COLUMN 15   PIC X(10)
                   VALUE 'DATE'.
               10  COLUMN 30   PIC X(06)
                   VALUE 'TYPE'.
               10  COLUMN 45   PIC X(12)
                   VALUE 'AMOUNT'.
               10  COLUMN 62   PIC X(11)
                   VALUE 'DESCRIPTION'.

Key clauses within report groups:

LINE clause: Specifies which line number (relative to the group's position on the page) or line advancement: - LINE 1 — absolute line number within the page area - LINE PLUS 1 — advance one line from the current position - LINE PLUS 2 — skip a line (double-space)

COLUMN clause: Specifies the column position for a field: - COLUMN 1 — start at column 1 - COLUMN 45 — start at column 45

SOURCE clause: Names a data item whose value should be printed: - SOURCE IS WS-ACCOUNT-NUM — print the current value of WS-ACCOUNT-NUM

VALUE clause: Specifies a literal value: - VALUE 'TOTAL:' — print the literal text

SUM clause: Automatically accumulates values for control break totals: - SUM DETAIL-AMOUNT — sum the DETAIL-AMOUNT field from each detail line

GROUP INDICATE clause: Prints the field only on the first detail line of each control group (suppresses repeating values): - GROUP INDICATE — show this field only once per group

16.3.3 Complete PAGE HEADING Example

       01  TYPE IS PAGE HEADING.
           05  LINE 1.
               10  COLUMN 1    PIC X(40)
                   VALUE 'GLOBALBANK DAILY TRANSACTION REPORT'.
               10  COLUMN 100  PIC X(06) VALUE 'PAGE: '.
               10  COLUMN 106  PIC Z(4)9
                   SOURCE IS PAGE-COUNTER.
           05  LINE 2.
               10  COLUMN 1    PIC X(06) VALUE 'DATE: '.
               10  COLUMN 7    PIC 99/99/9999
                   SOURCE IS WS-REPORT-DATE.
               10  COLUMN 100  PIC X(06) VALUE 'TIME: '.
               10  COLUMN 106  PIC 99:99:99
                   SOURCE IS WS-REPORT-TIME.
           05  LINE 3.
               10  COLUMN 1    PIC X(132) VALUE ALL '-'.
           05  LINE 4.
               10  COLUMN 1    PIC X(10) VALUE 'ACCOUNT   '.
               10  COLUMN 15   PIC X(10) VALUE 'DATE      '.
               10  COLUMN 30   PIC X(04) VALUE 'TYPE'.
               10  COLUMN 40   PIC X(14) VALUE '        AMOUNT'.
               10  COLUMN 60   PIC X(30) VALUE 'DESCRIPTION'.

💡 PAGE-COUNTER: Report Writer automatically maintains a special register called PAGE-COUNTER that tracks the current page number. You can reference it with SOURCE IS PAGE-COUNTER. It increments automatically with each new page.

16.3.4 DETAIL Group

The DETAIL group defines how individual data records appear:

       01  DETAIL-LINE TYPE IS DETAIL.
           05  LINE PLUS 1.
               10  COLUMN 1    PIC X(10)
                   SOURCE IS TXN-ACCT-NUM
                   GROUP INDICATE.
               10  COLUMN 15   PIC 9999/99/99
                   SOURCE IS TXN-DATE.
               10  COLUMN 30   PIC X(04)
                   SOURCE IS TXN-TYPE-DESC.
               10  COLUMN 40   PIC -(7)9.99
                   SOURCE IS TXN-AMOUNT.
               10  COLUMN 60   PIC X(30)
                   SOURCE IS TXN-DESC.

The GROUP INDICATE on the account number means it prints only on the first detail line for each account — subsequent lines for the same account show blank in the account column. This is a common report formatting convention that improves readability.

16.3.5 CONTROL HEADING Group

Control headings print when a control field changes, before the first detail of the new group:

       01  TYPE IS CONTROL HEADING ACCT-NUMBER.
           05  LINE PLUS 2.
               10  COLUMN 1    PIC X(20)
                   VALUE '*** ACCOUNT: '.
               10  COLUMN 21   PIC X(10)
                   SOURCE IS TXN-ACCT-NUM.
               10  COLUMN 35   PIC X(15)
                   SOURCE IS WS-ACCT-NAME.

16.3.6 CONTROL FOOTING Group

Control footings print when a control field changes, after the last detail of the ending group:

       01  TYPE IS CONTROL FOOTING ACCT-NUMBER.
           05  LINE PLUS 1.
               10  COLUMN 1    PIC X(132) VALUE ALL '-'.
           05  LINE PLUS 1.
               10  COLUMN 1    PIC X(25)
                   VALUE '    ACCOUNT TOTAL:'.
               10  COLUMN 40   PIC -(9)9.99
                   SUM TXN-AMOUNT.
               10  COLUMN 60   PIC X(10) VALUE 'TRANS CNT:'.
               10  COLUMN 72   PIC Z(5)9
                   SUM TRANS-COUNTER.

The SUM clause is where Report Writer truly shines. SUM TXN-AMOUNT automatically accumulates TXN-AMOUNT from each detail line processed since the last control break for this control field. You do not write any accumulation code — Report Writer handles it.

16.3.7 CONTROL FOOTING FINAL — Grand Totals

       01  TYPE IS CONTROL FOOTING FINAL.
           05  LINE PLUS 2.
               10  COLUMN 1    PIC X(132) VALUE ALL '='.
           05  LINE PLUS 1.
               10  COLUMN 1    PIC X(25)
                   VALUE '    GRAND TOTAL:'.
               10  COLUMN 40   PIC -(11)9.99
                   SUM TXN-AMOUNT.
               10  COLUMN 60   PIC X(12)
                   VALUE 'TOTAL TRANS:'.
               10  COLUMN 74   PIC Z(7)9
                   SUM TRANS-COUNTER.

SUM in FINAL footings works hierarchically. It sums the values from the next-lower control footing's SUM fields. This means the grand total automatically equals the sum of all account totals.

16.3.8 PAGE FOOTING

       01  TYPE IS PAGE FOOTING.
           05  LINE 58.
               10  COLUMN 1    PIC X(132) VALUE ALL '-'.
           05  LINE 59.
               10  COLUMN 50   PIC X(20)
                   VALUE '*** CONTINUED ***'.
               10  COLUMN 120  PIC X(06) VALUE 'PAGE '.
               10  COLUMN 126  PIC Z(4)9
                   SOURCE IS PAGE-COUNTER.

16.3.9 REPORT HEADING and REPORT FOOTING

These optional groups appear once — at the very beginning and very end of the report:

       01  TYPE IS REPORT HEADING.
           05  LINE 1.
               10  COLUMN 40   PIC X(50)
                   VALUE 'GLOBALBANK FINANCIAL SYSTEMS'.
           05  LINE 3.
               10  COLUMN 30   PIC X(70)
                   VALUE 'DAILY TRANSACTION REPORT - CONFIDENTIAL'.

       01  TYPE IS REPORT FOOTING.
           05  LINE PLUS 3.
               10  COLUMN 40   PIC X(30)
                   VALUE '*** END OF REPORT ***'.
           05  LINE PLUS 1.
               10  COLUMN 40   PIC X(30)
                   VALUE 'GENERATED BY SYSTEM GBRPT01'.

16.4 The PROCEDURE DIVISION with Report Writer

The PROCEDURE DIVISION for a Report Writer program is remarkably simple. Here is the complete logic for a transaction report:

       PROCEDURE DIVISION.
       0000-MAIN.
           OPEN INPUT  TRANSACTION-FILE
           OPEN OUTPUT REPORT-FILE
           INITIATE DAILY-TRANSACTION-REPORT

           READ TRANSACTION-FILE
               AT END SET END-OF-FILE TO TRUE
           END-READ

           PERFORM 1000-PROCESS-TRANSACTIONS
               UNTIL END-OF-FILE

           TERMINATE DAILY-TRANSACTION-REPORT
           CLOSE TRANSACTION-FILE
           CLOSE REPORT-FILE
           STOP RUN.

       1000-PROCESS-TRANSACTIONS.
           MOVE TXN-TYPE TO WS-TYPE-DESC
           GENERATE DETAIL-LINE
           READ TRANSACTION-FILE
               AT END SET END-OF-FILE TO TRUE
           END-READ.

That is the entire PROCEDURE DIVISION. No page-overflow checking, no control-break detection, no total accumulation, no header/footer management. The single GENERATE DETAIL-LINE statement triggers all of it automatically.

Best Practice: The simplicity of the PROCEDURE DIVISION is Report Writer's greatest advantage. When you see a Report Writer program, you know immediately that the PROCEDURE DIVISION is about reading data and basic transformations, not about report mechanics. All report structure is in the DATA DIVISION where it belongs.


16.5 GENERATE Statement: Detail vs. Summary

The GENERATE statement has two forms:

16.5.1 GENERATE detail-group-name

           GENERATE DETAIL-LINE

This produces a detail line on the report. It also: - Checks for control breaks and produces control footings/headings as needed - Checks for page overflow and produces page footings/headings as needed - Accumulates SUM fields - Increments LINE-COUNTER

16.5.2 GENERATE report-name

           GENERATE DAILY-TRANSACTION-REPORT

This produces a summary report — control footings with SUM values are produced, but no detail lines. This is useful when you want only subtotals and grand totals, not individual records.

For example, to produce a summary report showing only account totals and a grand total (no individual transaction lines), simply change the GENERATE:

       1000-PROCESS-TRANSACTIONS.
           GENERATE DAILY-TRANSACTION-REPORT
           READ TRANSACTION-FILE
               AT END SET END-OF-FILE TO TRUE
           END-READ.

💡 Dual Reports: Some programs produce both a detail report and a summary report from the same data. Define two RD entries (linked to two output files) and issue GENERATE for both in the processing loop.


16.6 SUM Clause: Automatic Accumulation

The SUM clause is Report Writer's most powerful feature. It automatically accumulates values without any procedural code.

16.6.1 SUM in CONTROL FOOTING

       01  TYPE IS CONTROL FOOTING BRANCH-CODE.
           05  LINE PLUS 1.
               10  COLUMN 40   PIC -(9)9.99
                   SUM DETAIL-AMOUNT.

This sums DETAIL-AMOUNT (a field defined in the DETAIL group) for every detail line generated within the current BRANCH-CODE group.

16.6.2 SUM Cascading (Rolling Up)

In a multi-level control hierarchy, SUM values cascade upward:

      * Account-level subtotal
       01  TYPE IS CONTROL FOOTING ACCT-NUMBER.
           05  LINE PLUS 1.
               10  COLUMN 40   PIC -(9)9.99
                   SUM DETAIL-AMOUNT.         *> Sums detail amounts

      * Branch-level subtotal
       01  TYPE IS CONTROL FOOTING BRANCH-CODE.
           05  LINE PLUS 1.
               10  COLUMN 40   PIC -(11)9.99
                   SUM DETAIL-AMOUNT.         *> Sums account subtotals

      * Grand total
       01  TYPE IS CONTROL FOOTING FINAL.
           05  LINE PLUS 1.
               10  COLUMN 40   PIC -(13)9.99
                   SUM DETAIL-AMOUNT.         *> Sums branch subtotals

When SUM references the same field at multiple control levels, it works hierarchically: - Account CF: Sums individual detail amounts within each account - Branch CF: Sums the account subtotals (not the individual details) within each branch - FINAL CF: Sums the branch subtotals

This cascading behavior is automatic and correct. You do not need to manage intermediate accumulators.

16.6.3 TRANS-COUNTER Pattern

To count records within a control group, use a detail-level counter with SUM:

In the DETAIL group, include a counter field set to 1:

       01  DETAIL-LINE TYPE IS DETAIL.
           05  LINE PLUS 1.
               10  TRANS-COUNTER PIC 9 VALUE 1
                   COLUMN 999.               *> Off-page (not printed)
               10  COLUMN 1    PIC X(10)
                   SOURCE IS TXN-ACCT-NUM.
      *        ... other detail fields ...

Then SUM it in the control footing:

       01  TYPE IS CONTROL FOOTING ACCT-NUMBER.
           05  LINE PLUS 1.
               10  COLUMN 60   PIC Z(5)9
                   SUM TRANS-COUNTER.

📊 Note: Placing TRANS-COUNTER at a column beyond the page width (COLUMN 999 with a PAGE LIMIT of 132) effectively makes it a non-printing field. It exists in the report group's record but does not appear on the printed page.


16.7 Special Registers: PAGE-COUNTER and LINE-COUNTER

Report Writer maintains two special registers per report:

16.7.1 PAGE-COUNTER

  • Automatically incremented when a new page starts
  • Can be referenced with SOURCE IS PAGE-COUNTER
  • Can be SET or MOVEd to if you need to override (rarely needed)
  • Starts at 1

16.7.2 LINE-COUNTER

  • Tracks the current line position on the page
  • Used internally by Report Writer for page overflow detection
  • Can be referenced in PROCEDURE DIVISION but rarely needed
  • Reset to 0 at the start of each page

If you have multiple reports, qualify the register:

           SOURCE IS PAGE-COUNTER OF DAILY-TRANSACTION-REPORT

16.8 Complete Report Writer Program: GlobalBank Daily Report

Let us build a complete program that produces GlobalBank's daily transaction report with account-level subtotals and grand totals.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. GBRPT01.
      *================================================================
      * Program:  GBRPT01
      * Purpose:  GlobalBank daily transaction report using
      *           Report Writer facility
      * Author:   Maria Chen
      * Input:    Sorted transaction file (by account, then date)
      * Output:   132-column report with control breaks
      *================================================================

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT TRANS-FILE  ASSIGN TO TXNIN.
           SELECT REPORT-FILE ASSIGN TO RPTOUT.

       DATA DIVISION.
       FILE SECTION.

       FD  TRANS-FILE
           RECORDING MODE IS F
           BLOCK CONTAINS 0 RECORDS.
       01  TRANS-REC.
           05  TXN-ACCT-NUM   PIC X(10).
           05  TXN-DATE        PIC 9(08).
           05  TXN-TIME        PIC 9(06).
           05  TXN-TYPE        PIC X(01).
           05  TXN-AMOUNT      PIC S9(09)V99 COMP-3.
           05  TXN-BRANCH      PIC X(05).
           05  TXN-TELLER      PIC X(06).
           05  TXN-DESC        PIC X(30).
           05  FILLER          PIC X(22).

       FD  REPORT-FILE
           REPORT IS DAILY-TXN-REPORT.
       01  REPORT-LINE         PIC X(132).

       WORKING-STORAGE SECTION.
       01  WS-FLAGS.
           05  WS-EOF          PIC X VALUE 'N'.
               88  END-OF-FILE VALUE 'Y'.
       01  WS-REPORT-DATE      PIC 9(08).
       01  WS-REPORT-TIME      PIC 9(06).
       01  WS-TYPE-DESC        PIC X(12).

       REPORT SECTION.
       RD  DAILY-TXN-REPORT
           CONTROLS ARE FINAL
                       TXN-ACCT-NUM
           PAGE LIMIT IS 60 LINES
           HEADING 1
           FIRST DETAIL 6
           LAST DETAIL 54
           FOOTING 58.

      *--- REPORT HEADING (first page only) ---
       01  TYPE IS REPORT HEADING.
           05  LINE 1.
               10  COLUMN 30   PIC X(50)
                   VALUE 'G L O B A L B A N K'.
           05  LINE 3.
               10  COLUMN 25   PIC X(60)
                   VALUE
                   'DAILY TRANSACTION REPORT - CONFIDENTIAL'.

      *--- PAGE HEADING (each page) ---
       01  TYPE IS PAGE HEADING.
           05  LINE 1.
               10  COLUMN 1    PIC X(40)
                   VALUE 'GLOBALBANK DAILY TRANSACTION REPORT'.
               10  COLUMN 100  PIC X(06) VALUE 'PAGE: '.
               10  COLUMN 106  PIC ZZZ9
                   SOURCE IS PAGE-COUNTER.
           05  LINE 2.
               10  COLUMN 1    PIC X(16) VALUE 'REPORT DATE:    '.
               10  COLUMN 17   PIC 9999/99/99
                   SOURCE IS WS-REPORT-DATE.
           05  LINE 3.
               10  COLUMN 1    PIC X(132) VALUE ALL '-'.
           05  LINE 4.
               10  COLUMN 1    PIC X(10) VALUE 'ACCOUNT   '.
               10  COLUMN 15   PIC X(10) VALUE 'DATE      '.
               10  COLUMN 30   PIC X(12) VALUE 'TYPE        '.
               10  COLUMN 48   PIC X(14) VALUE '        AMOUNT'.
               10  COLUMN 68   PIC X(06) VALUE 'BRANCH'.
               10  COLUMN 80   PIC X(30) VALUE 'DESCRIPTION'.
           05  LINE 5.
               10  COLUMN 1    PIC X(132) VALUE ALL '-'.

      *--- CONTROL HEADING for account group ---
       01  TYPE IS CONTROL HEADING TXN-ACCT-NUM.
           05  LINE PLUS 1.
               10  COLUMN 1    PIC X(14) VALUE '  ACCOUNT:  '.
               10  COLUMN 15   PIC X(10)
                   SOURCE IS TXN-ACCT-NUM.

      *--- DETAIL LINE ---
       01  DETAIL-LINE TYPE IS DETAIL.
           05  LINE PLUS 1.
               10  RPT-TRANS-COUNTER  PIC 9 VALUE 1.
               10  COLUMN 1    PIC X(10)
                   SOURCE IS TXN-ACCT-NUM
                   GROUP INDICATE.
               10  COLUMN 15   PIC 9999/99/99
                   SOURCE IS TXN-DATE.
               10  COLUMN 30   PIC X(12)
                   SOURCE IS WS-TYPE-DESC.
               10  RPT-DETAIL-AMT
                   COLUMN 45   PIC -(9)9.99
                   SOURCE IS TXN-AMOUNT.
               10  COLUMN 68   PIC X(05)
                   SOURCE IS TXN-BRANCH.
               10  COLUMN 80   PIC X(30)
                   SOURCE IS TXN-DESC.

      *--- CONTROL FOOTING for account subtotals ---
       01  TYPE IS CONTROL FOOTING TXN-ACCT-NUM.
           05  LINE PLUS 1.
               10  COLUMN 1    PIC X(132) VALUE ALL '-'.
           05  LINE PLUS 1.
               10  COLUMN 5    PIC X(20)
                   VALUE 'ACCOUNT SUBTOTAL:'.
               10  COLUMN 45   PIC -(9)9.99
                   SUM RPT-DETAIL-AMT.
               10  COLUMN 68   PIC X(07) VALUE 'COUNT: '.
               10  COLUMN 75   PIC Z(5)9
                   SUM RPT-TRANS-COUNTER.

      *--- CONTROL FOOTING FINAL (grand totals) ---
       01  TYPE IS CONTROL FOOTING FINAL.
           05  LINE PLUS 2.
               10  COLUMN 1    PIC X(132) VALUE ALL '='.
           05  LINE PLUS 1.
               10  COLUMN 5    PIC X(20)
                   VALUE '*** GRAND TOTAL:'.
               10  COLUMN 45   PIC -(11)9.99
                   SUM RPT-DETAIL-AMT.
               10  COLUMN 68   PIC X(07) VALUE 'COUNT: '.
               10  COLUMN 75   PIC Z(7)9
                   SUM RPT-TRANS-COUNTER.
           05  LINE PLUS 1.
               10  COLUMN 1    PIC X(132) VALUE ALL '='.

      *--- PAGE FOOTING ---
       01  TYPE IS PAGE FOOTING.
           05  LINE 58.
               10  COLUMN 55   PIC X(20)
                   VALUE '*** CONTINUED ***'.

      *--- REPORT FOOTING ---
       01  TYPE IS REPORT FOOTING.
           05  LINE PLUS 3.
               10  COLUMN 45   PIC X(30)
                   VALUE '*** END OF REPORT ***'.

       PROCEDURE DIVISION.
       0000-MAIN.
           ACCEPT WS-REPORT-DATE FROM DATE YYYYMMDD
           ACCEPT WS-REPORT-TIME FROM TIME

           OPEN INPUT  TRANS-FILE
           OPEN OUTPUT REPORT-FILE
           INITIATE DAILY-TXN-REPORT

           READ TRANS-FILE
               AT END SET END-OF-FILE TO TRUE
           END-READ

           PERFORM 1000-PROCESS-TRANSACTIONS
               UNTIL END-OF-FILE

           TERMINATE DAILY-TXN-REPORT
           CLOSE TRANS-FILE
                 REPORT-FILE
           STOP RUN.

       1000-PROCESS-TRANSACTIONS.
           PERFORM 1100-SET-TYPE-DESC
           GENERATE DETAIL-LINE
           READ TRANS-FILE
               AT END SET END-OF-FILE TO TRUE
           END-READ.

       1100-SET-TYPE-DESC.
           EVALUATE TXN-TYPE
               WHEN 'D' MOVE 'DEPOSIT'      TO WS-TYPE-DESC
               WHEN 'W' MOVE 'WITHDRAWAL'   TO WS-TYPE-DESC
               WHEN 'T' MOVE 'TRANSFER'     TO WS-TYPE-DESC
               WHEN 'F' MOVE 'FEE'          TO WS-TYPE-DESC
               WHEN 'I' MOVE 'INTEREST'     TO WS-TYPE-DESC
               WHEN OTHER MOVE 'UNKNOWN'    TO WS-TYPE-DESC
           END-EVALUATE.

🧪 Try It Yourself: Enter this program in your Student Mainframe Lab. Create a sorted transaction file with at least 30 records covering 5-6 different accounts. Compile and run the program, then examine the report output. Verify that: - Page headings appear at the top of each page - Account numbers show only on the first detail line (GROUP INDICATE) - Account subtotals are correct - The grand total equals the sum of all account subtotals - Page numbering is correct


16.9 Manual Control Break Processing: The Alternative

Before Report Writer, and in many shops that do not use it, reports are written with manual control-break logic. Understanding this approach helps you appreciate Report Writer's value — and prepares you for maintaining the many production programs that use it.

16.9.1 The Manual Approach

A manual report program must handle:

  1. Line counting: Track current line position, detect page overflow
  2. Page handling: Write headers, footers, advance to new pages
  3. Control break detection: Compare current key to previous key
  4. Accumulation: Maintain subtotals and grand totals manually
  5. Detail formatting: Build each output line field by field

16.9.2 Manual Control Break Example

Here is the equivalent of the Report Writer program above, written manually:

       WORKING-STORAGE SECTION.
       01  WS-LINE-COUNT       PIC 99 VALUE 99.
       01  WS-PAGE-COUNT       PIC 9(04) VALUE ZERO.
       01  WS-LINES-PER-PAGE   PIC 99 VALUE 55.
       01  WS-PREV-ACCT        PIC X(10) VALUE HIGH-VALUES.
       01  WS-ACCT-TOTAL       PIC S9(11)V99 VALUE ZERO.
       01  WS-GRAND-TOTAL      PIC S9(13)V99 VALUE ZERO.
       01  WS-ACCT-COUNT       PIC 9(07) VALUE ZERO.
       01  WS-GRAND-COUNT      PIC 9(09) VALUE ZERO.
       01  WS-FIRST-RECORD     PIC X VALUE 'Y'.

       01  WS-HEADER-1.
           05  FILLER          PIC X(40)
               VALUE 'GLOBALBANK DAILY TRANSACTION REPORT'.
           05  FILLER          PIC X(60) VALUE SPACES.
           05  FILLER          PIC X(06) VALUE 'PAGE: '.
           05  WH1-PAGE        PIC ZZZ9.
           05  FILLER          PIC X(22) VALUE SPACES.

       01  WS-DETAIL-LINE.
           05  WD-ACCT-NUM     PIC X(10).
           05  FILLER          PIC X(05) VALUE SPACES.
           05  WD-DATE         PIC 9999/99/99.
           05  FILLER          PIC X(05) VALUE SPACES.
           05  WD-TYPE         PIC X(12).
           05  FILLER          PIC X(03) VALUE SPACES.
           05  WD-AMOUNT       PIC -(9)9.99.
           05  FILLER          PIC X(05) VALUE SPACES.
           05  WD-DESC         PIC X(30).
           05  FILLER          PIC X(37) VALUE SPACES.

       01  WS-SUBTOTAL-LINE.
           05  FILLER          PIC X(05) VALUE SPACES.
           05  FILLER          PIC X(20)
               VALUE 'ACCOUNT SUBTOTAL:'.
           05  FILLER          PIC X(20) VALUE SPACES.
           05  WS-SUB-AMT      PIC -(9)9.99.
           05  FILLER          PIC X(05) VALUE SPACES.
           05  FILLER          PIC X(07) VALUE 'COUNT: '.
           05  WS-SUB-CNT      PIC Z(5)9.
           05  FILLER          PIC X(52) VALUE SPACES.

       PROCEDURE DIVISION.
       0000-MAIN.
           OPEN INPUT  TRANSACTION-FILE
           OPEN OUTPUT REPORT-FILE

           READ TRANSACTION-FILE
               AT END SET END-OF-FILE TO TRUE
           END-READ

           PERFORM 1000-PROCESS-TRANSACTIONS
               UNTIL END-OF-FILE

           IF WS-FIRST-RECORD = 'N'
               PERFORM 3000-ACCOUNT-BREAK
               PERFORM 4000-GRAND-TOTALS
           END-IF

           CLOSE TRANSACTION-FILE
                 REPORT-FILE
           STOP RUN.

       1000-PROCESS-TRANSACTIONS.
           IF TXN-ACCT-NUM NOT = WS-PREV-ACCT
               IF WS-FIRST-RECORD = 'N'
                   PERFORM 3000-ACCOUNT-BREAK
               END-IF
               MOVE TXN-ACCT-NUM TO WS-PREV-ACCT
               MOVE ZERO TO WS-ACCT-TOTAL
               MOVE ZERO TO WS-ACCT-COUNT
               MOVE 'N' TO WS-FIRST-RECORD
           END-IF

           IF WS-LINE-COUNT >= WS-LINES-PER-PAGE
               PERFORM 2000-PAGE-HEADERS
           END-IF

           PERFORM 1100-FORMAT-DETAIL
           WRITE REPORT-LINE FROM WS-DETAIL-LINE
               AFTER ADVANCING 1 LINE
           ADD 1 TO WS-LINE-COUNT
           ADD TXN-AMOUNT TO WS-ACCT-TOTAL
           ADD 1 TO WS-ACCT-COUNT

           READ TRANSACTION-FILE
               AT END SET END-OF-FILE TO TRUE
           END-READ.

       1100-FORMAT-DETAIL.
           INITIALIZE WS-DETAIL-LINE
           IF TXN-ACCT-NUM NOT = WS-PREV-ACCT
               MOVE TXN-ACCT-NUM TO WD-ACCT-NUM
           ELSE
               MOVE SPACES TO WD-ACCT-NUM
           END-IF
           MOVE TXN-DATE TO WD-DATE
           EVALUATE TXN-TYPE
               WHEN 'D' MOVE 'DEPOSIT'    TO WD-TYPE
               WHEN 'W' MOVE 'WITHDRAWAL' TO WD-TYPE
               WHEN 'T' MOVE 'TRANSFER'   TO WD-TYPE
               WHEN OTHER MOVE 'UNKNOWN'  TO WD-TYPE
           END-EVALUATE
           MOVE TXN-AMOUNT TO WD-AMOUNT
           MOVE TXN-DESC TO WD-DESC.

       2000-PAGE-HEADERS.
           ADD 1 TO WS-PAGE-COUNT
           MOVE WS-PAGE-COUNT TO WH1-PAGE
           WRITE REPORT-LINE FROM WS-HEADER-1
               AFTER ADVANCING PAGE
           MOVE SPACES TO REPORT-LINE
           WRITE REPORT-LINE AFTER ADVANCING 1 LINE
      *    ... column headers, separator lines ...
           MOVE 5 TO WS-LINE-COUNT.

       3000-ACCOUNT-BREAK.
           MOVE WS-ACCT-TOTAL TO WS-SUB-AMT
           MOVE WS-ACCT-COUNT TO WS-SUB-CNT
           WRITE REPORT-LINE FROM WS-SUBTOTAL-LINE
               AFTER ADVANCING 1 LINE
           ADD 1 TO WS-LINE-COUNT
           ADD WS-ACCT-TOTAL TO WS-GRAND-TOTAL
           ADD WS-ACCT-COUNT TO WS-GRAND-COUNT.

       4000-GRAND-TOTALS.
      *    ... format and write grand total lines ...
           CONTINUE.

📊 Comparison by the Numbers:

Aspect Report Writer Manual
PROCEDURE DIVISION lines ~20 ~80+
DATA DIVISION for report ~80 (REPORT SECTION) ~80 (WORKING-STORAGE lines)
Total report-related code ~100 ~160+
Accumulation logic Automatic (SUM) Manual (ADD statements)
Page overflow Automatic Manual (IF line-count >= limit)
Control break detection Automatic (CONTROLS) Manual (IF key NOT = prev-key)
Bug risk Low (declarative) Higher (procedural)

⚖️ When Each Approach Is Appropriate:

Use Report Writer when: - The report has a regular structure with clear control breaks - Multiple control levels are needed - Automatic accumulation is valuable - The report is the primary output of the program

Use manual processing when: - The report has irregular formatting that Report Writer cannot express - You need fine-grained control over every line - The report is a secondary output of a complex program - Your shop does not support or encourage Report Writer - The report requires conditional formatting based on data values that vary line-to-line

🔵 Maria Chen's Perspective: "I've maintained both kinds of report programs for twenty years. Report Writer programs are easier to understand and modify for standard reports. Manual programs give you more flexibility but are harder to maintain. My rule: if the report has a standard tabular layout with control breaks, use Report Writer. If it's a free-form letter or a report with highly conditional formatting, write it manually."


16.10 Multi-Level Control Break Report: MedClaim Provider Payment Summary

Let us build a more complex example: MedClaim's provider payment summary report with two control levels — region and provider.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. MCRPT01.
      *================================================================
      * Program:  MCRPT01
      * Purpose:  MedClaim provider payment summary report
      *           with region and provider control breaks
      * Author:   James Okafor
      *================================================================

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT PAYMENT-FILE ASSIGN TO PAYIN.
           SELECT REPORT-FILE  ASSIGN TO RPTOUT.

       DATA DIVISION.
       FILE SECTION.

       FD  PAYMENT-FILE
           RECORDING MODE IS F
           BLOCK CONTAINS 0 RECORDS.
       01  PAY-REC.
           05  PAY-REGION      PIC X(04).
           05  PAY-PROVIDER    PIC X(10).
           05  PAY-PROV-NAME   PIC X(30).
           05  PAY-CLAIM-DATE  PIC 9(08).
           05  PAY-CLAIM-ID    PIC X(15).
           05  PAY-AMOUNT      PIC S9(07)V99 COMP-3.
           05  PAY-STATUS      PIC X(02).
           05  FILLER          PIC X(20).

       FD  REPORT-FILE
           REPORT IS PROVIDER-PAYMENT-RPT.
       01  REPORT-LINE         PIC X(132).

       WORKING-STORAGE SECTION.
       01  WS-FLAGS.
           05  WS-EOF          PIC X VALUE 'N'.
               88  END-OF-FILE VALUE 'Y'.
       01  WS-REPORT-DATE      PIC 9(08).
       01  WS-STATUS-DESC      PIC X(10).

       REPORT SECTION.
       RD  PROVIDER-PAYMENT-RPT
           CONTROLS ARE FINAL
                       PAY-REGION
                       PAY-PROVIDER
           PAGE LIMIT IS 66 LINES
           HEADING 1
           FIRST DETAIL 8
           LAST DETAIL 58
           FOOTING 63.

      *--- PAGE HEADING ---
       01  TYPE IS PAGE HEADING.
           05  LINE 1.
               10  COLUMN 1    PIC X(45)
                   VALUE 'MEDCLAIM HEALTH SERVICES'.
               10  COLUMN 100  PIC X(06) VALUE 'PAGE: '.
               10  COLUMN 106  PIC ZZZ9
                   SOURCE IS PAGE-COUNTER.
           05  LINE 2.
               10  COLUMN 1    PIC X(45)
                   VALUE 'PROVIDER PAYMENT SUMMARY REPORT'.
               10  COLUMN 100  PIC X(06) VALUE 'DATE: '.
               10  COLUMN 106  PIC 9999/99/99
                   SOURCE IS WS-REPORT-DATE.
           05  LINE 4.
               10  COLUMN 1    PIC X(132) VALUE ALL '-'.
           05  LINE 5.
               10  COLUMN 1    PIC X(06)  VALUE 'REGION'.
               10  COLUMN 10   PIC X(10)  VALUE 'PROVIDER'.
               10  COLUMN 25   PIC X(12)  VALUE 'CLAIM DATE'.
               10  COLUMN 42   PIC X(15)  VALUE 'CLAIM ID'.
               10  COLUMN 62   PIC X(10)  VALUE 'STATUS'.
               10  COLUMN 78   PIC X(14)  VALUE '        AMOUNT'.
           05  LINE 6.
               10  COLUMN 1    PIC X(132) VALUE ALL '-'.

      *--- CONTROL HEADING for region ---
       01  TYPE IS CONTROL HEADING PAY-REGION.
           05  LINE PLUS 2.
               10  COLUMN 1    PIC X(20)
                   VALUE '=== REGION: '.
               10  COLUMN 21   PIC X(04)
                   SOURCE IS PAY-REGION.
               10  COLUMN 26   PIC X(04) VALUE ' ==='.

      *--- CONTROL HEADING for provider ---
       01  TYPE IS CONTROL HEADING PAY-PROVIDER.
           05  LINE PLUS 1.
               10  COLUMN 5    PIC X(12)
                   VALUE '  PROVIDER: '.
               10  COLUMN 17   PIC X(10)
                   SOURCE IS PAY-PROVIDER.
               10  COLUMN 30   PIC X(30)
                   SOURCE IS PAY-PROV-NAME.

      *--- DETAIL LINE ---
       01  PAYMENT-DETAIL TYPE IS DETAIL.
           05  LINE PLUS 1.
               10  RPT-PAY-COUNTER PIC 9 VALUE 1.
               10  COLUMN 1    PIC X(04)
                   SOURCE IS PAY-REGION
                   GROUP INDICATE.
               10  COLUMN 10   PIC X(10)
                   SOURCE IS PAY-PROVIDER
                   GROUP INDICATE.
               10  COLUMN 25   PIC 9999/99/99
                   SOURCE IS PAY-CLAIM-DATE.
               10  COLUMN 42   PIC X(15)
                   SOURCE IS PAY-CLAIM-ID.
               10  COLUMN 62   PIC X(10)
                   SOURCE IS WS-STATUS-DESC.
               10  RPT-PAY-AMOUNT
                   COLUMN 78   PIC -(7)9.99
                   SOURCE IS PAY-AMOUNT.

      *--- CONTROL FOOTING for provider subtotals ---
       01  TYPE IS CONTROL FOOTING PAY-PROVIDER.
           05  LINE PLUS 1.
               10  COLUMN 5    PIC X(90) VALUE ALL '-'.
           05  LINE PLUS 1.
               10  COLUMN 5    PIC X(20)
                   VALUE '  PROVIDER TOTAL:'.
               10  COLUMN 78   PIC -(9)9.99
                   SUM RPT-PAY-AMOUNT.
               10  COLUMN 100  PIC X(08) VALUE 'CLAIMS: '.
               10  COLUMN 108  PIC Z(5)9
                   SUM RPT-PAY-COUNTER.

      *--- CONTROL FOOTING for region subtotals ---
       01  TYPE IS CONTROL FOOTING PAY-REGION.
           05  LINE PLUS 1.
               10  COLUMN 1    PIC X(100) VALUE ALL '='.
           05  LINE PLUS 1.
               10  COLUMN 1    PIC X(20)
                   VALUE '  REGION TOTAL:'.
               10  COLUMN 78   PIC -(11)9.99
                   SUM RPT-PAY-AMOUNT.
               10  COLUMN 100  PIC X(08) VALUE 'CLAIMS: '.
               10  COLUMN 108  PIC Z(7)9
                   SUM RPT-PAY-COUNTER.

      *--- CONTROL FOOTING FINAL ---
       01  TYPE IS CONTROL FOOTING FINAL.
           05  LINE PLUS 2.
               10  COLUMN 1    PIC X(132) VALUE ALL '='.
           05  LINE PLUS 1.
               10  COLUMN 1    PIC X(25)
                   VALUE '  *** GRAND TOTAL: ***'.
               10  COLUMN 78   PIC -(13)9.99
                   SUM RPT-PAY-AMOUNT.
               10  COLUMN 100  PIC X(08) VALUE 'CLAIMS: '.
               10  COLUMN 108  PIC Z(9)9
                   SUM RPT-PAY-COUNTER.
           05  LINE PLUS 1.
               10  COLUMN 1    PIC X(132) VALUE ALL '='.

      *--- REPORT FOOTING ---
       01  TYPE IS REPORT FOOTING.
           05  LINE PLUS 3.
               10  COLUMN 45   PIC X(30)
                   VALUE '*** END OF REPORT ***'.

       PROCEDURE DIVISION.
       0000-MAIN.
           ACCEPT WS-REPORT-DATE FROM DATE YYYYMMDD

           OPEN INPUT  PAYMENT-FILE
           OPEN OUTPUT REPORT-FILE
           INITIATE PROVIDER-PAYMENT-RPT

           READ PAYMENT-FILE
               AT END SET END-OF-FILE TO TRUE
           END-READ

           PERFORM 1000-PROCESS-PAYMENTS
               UNTIL END-OF-FILE

           TERMINATE PROVIDER-PAYMENT-RPT
           CLOSE PAYMENT-FILE
                 REPORT-FILE
           STOP RUN.

       1000-PROCESS-PAYMENTS.
           PERFORM 1100-SET-STATUS-DESC
           GENERATE PAYMENT-DETAIL
           READ PAYMENT-FILE
               AT END SET END-OF-FILE TO TRUE
           END-READ.

       1100-SET-STATUS-DESC.
           EVALUATE PAY-STATUS
               WHEN 'PD' MOVE 'PAID'      TO WS-STATUS-DESC
               WHEN 'DN' MOVE 'DENIED'    TO WS-STATUS-DESC
               WHEN 'PN' MOVE 'PENDING'   TO WS-STATUS-DESC
               WHEN 'AP' MOVE 'APPROVED'  TO WS-STATUS-DESC
               WHEN OTHER MOVE 'UNKNOWN'  TO WS-STATUS-DESC
           END-EVALUATE.

⚠️ Input Must Be Sorted: This program requires the payment file to be sorted by region (ascending), then provider (ascending), then claim date (ascending). Running it against unsorted data will produce incorrect subtotals. This is a common error — always verify sort order before running a control-break report.


16.11 Advanced Report Writer Features

16.11.1 USE BEFORE REPORTING

The USE BEFORE REPORTING declarative allows you to execute procedural code just before a specific report group is generated:

       DECLARATIVES.
       ACCT-HEADING-SECTION SECTION.
           USE BEFORE REPORTING ACCT-HEADING-GROUP.
       ACCT-HEADING-PARA.
           PERFORM LOOKUP-ACCOUNT-NAME.
       END DECLARATIVES.

This is useful for looking up reference data (like account names) that should appear in control headings.

16.11.2 PRESENT WHEN Clause

Some compilers support PRESENT WHEN, which conditionally includes a field:

               10  COLUMN 80   PIC X(10)
                   VALUE '*** HIGH ***'
                   PRESENT WHEN TXN-AMOUNT > 10000.

📊 Compiler Support: PRESENT WHEN is part of the COBOL 2002 and 2014 standards but is not supported by all compilers. Check your compiler's documentation.

16.11.3 Multiple Reports per Program

A single program can produce multiple reports:

       FD  DETAIL-REPORT-FILE
           REPORT IS TXN-DETAIL-REPORT.
       FD  SUMMARY-REPORT-FILE
           REPORT IS TXN-SUMMARY-REPORT.

In the PROCEDURE DIVISION, INITIATE both, GENERATE from both in the processing loop (GENERATE DETAIL-LINE for the detail report, GENERATE TXN-SUMMARY-REPORT for the summary), and TERMINATE both.

16.11.4 Summary Reporting

A summary-only report omits detail lines entirely. Instead of GENERATE detail-name, use GENERATE report-name:

       1000-PROCESS-TRANSACTIONS.
           GENERATE DAILY-TXN-REPORT
           READ TRANS-FILE
               AT END SET END-OF-FILE TO TRUE
           END-READ.

This produces only control headings, control footings (with SUM values), and page/report headings and footings. No detail lines appear.


16.12 Report Writer Limitations and Workarounds

Report Writer is powerful for standard tabular reports but has limitations:

16.12.1 Fixed Layout

Report Writer produces fixed-format output. It cannot generate HTML, XML, CSV, or any format other than fixed-position text lines. For modern output formats, manual processing is required.

16.12.2 No Conditional Formatting Per Line

You cannot conditionally change a detail line's format based on data values within the Report Writer declarations alone. The USE BEFORE REPORTING declarative provides a workaround by letting you set WORKING-STORAGE fields that the report group references via SOURCE.

16.12.3 Limited Arithmetic

SUM is powerful but limited to addition. If you need averages, percentages, or other computed values in control footings, use USE BEFORE REPORTING to compute them and place the results in WORKING-STORAGE fields referenced by SOURCE.

16.12.4 Compiler Support Varies

Not all COBOL compilers fully support Report Writer. IBM Enterprise COBOL supports it fully. GnuCOBOL supports Report Writer as of version 3.0. Micro Focus COBOL supports it. Always verify your compiler's level of support.

🧪 Try It Yourself — GnuCOBOL: If using GnuCOBOL, compile the GBRPT01 program with the -free flag (for free-format source if you prefer) or ensure your source is in standard fixed format. Create a test data file with at least 20 records across 4-5 accounts. Run the program and examine the report output. Experiment with changing PAGE LIMIT to 20 to force multiple pages and verify that page headings and footings work correctly.


16.13 Comparison: Report Writer vs. Manual for Common Patterns

16.13.1 Page Overflow

Report Writer: Automatic. When LAST DETAIL is reached, page footing prints, page advances, page heading prints. Zero code.

Manual: Track WS-LINE-COUNT. Before each WRITE, check if line count exceeds limit. If so, perform page-break paragraph that writes footer, advances page, writes header, resets counter.

16.13.2 Control Breaks

Report Writer: Automatic. Declare CONTROLS in RD, define CF groups with SUM. Control break detection, subtotal printing, and accumulator reset all happen automatically.

Manual: Save previous key value. Before processing each record, compare current key to saved key. If different, perform break paragraph that prints subtotals, resets accumulators, saves new key. Handle first-record and last-record edge cases separately.

16.13.3 Multi-Level Control Breaks

Report Writer: List multiple controls in major-to-minor order. Define CF for each level. Cascading breaks happen automatically.

Manual: Nested IF statements or cascading PERFORM logic. When a major break occurs, force all minor breaks first. The code complexity grows significantly with each additional control level.

16.13.4 Grand Totals

Report Writer: CONTROL FOOTING FINAL with SUM. Automatic cascading from lower-level sums.

Manual: Maintain a separate grand-total accumulator. Add to it at each minor break (or at each detail). Print at end of file.

🔴 Common Manual Report Bug: In manual control-break programs, the most common bug is forgetting to process the last control group. After the AT END condition is reached, the last group's subtotals must still be printed. Many programs handle this with an IF NOT FIRST-RECORD check after the main loop. Report Writer handles this automatically through TERMINATE.


16.14 Production Considerations

16.14.1 Print Control Characters

On mainframes, Report Writer output typically includes ASA carriage control characters in column 1. The WRITE ... AFTER ADVANCING pattern is handled automatically by Report Writer. Ensure your JCL DD statement for the report file specifies the correct DCB parameters:

//RPTOUT   DD SYSOUT=*,DCB=(RECFM=FBA,LRECL=133,BLKSIZE=1330)

The FBA recording mode means Fixed Blocked with ASA control characters. LRECL is 133 (132 data columns + 1 control character).

16.14.2 Performance

Report Writer adds minimal overhead compared to manual report generation. The automatic control break detection and SUM accumulation are simple operations. The primary performance factor is I/O — writing report lines — which is the same regardless of the approach.

16.14.3 Testing Report Writer Programs

Test report programs systematically:

  1. Empty input: Verify the program handles zero records gracefully
  2. Single record: Verify all report groups generate correctly
  3. Single control group: No control breaks — verify page handling
  4. Multiple groups: Verify control break detection and subtotals
  5. Page overflow: Provide enough records to force multiple pages
  6. Last page: Verify TERMINATE produces final footings and REPORT FOOTING

16.15 When NOT to Use Report Writer

Despite its elegance, Report Writer is not always the right choice:

  1. Non-standard output: PDF, HTML, XML, CSV, JSON — Report Writer produces fixed-format text only.

  2. Dynamic layouts: Reports where the number of columns varies based on data or parameters are difficult with Report Writer's fixed column positions.

  3. Cross-tabular reports: Pivot-table style reports where row data becomes column headers are not expressible in Report Writer.

  4. Highly conditional formatting: Reports where different record types require completely different line layouts are cumbersome in Report Writer (though USE BEFORE REPORTING can help).

  5. Interactive reports: Report Writer is designed for batch output, not screen-based or interactive reporting.

  6. Shop standards: Some shops have explicitly decided against Report Writer, preferring uniform manual coding for all reports. Follow your shop's standards.

⚖️ Derek's Reflection: "When I first learned Report Writer, I wanted to use it for everything. Maria taught me that the right tool depends on the job. Standard control-break reports? Report Writer every time. That one regulatory report with conditional columns and embedded graphs? That's a manual program calling a PDF library. Know when to use each tool."


16.16 Declarative vs. Procedural: A Philosophical Note

Report Writer represents one of COBOL's earliest experiments in declarative programming — describing what you want rather than how to produce it. The same philosophy appears in:

  • SQL: Describe the data you want, not how to retrieve it
  • CSS: Describe how elements should look, not the rendering algorithm
  • Report Writer: Describe the report structure, not the page-management logic

This is the essence of the chapter's theme — Readability is a Feature. Declarative code is inherently more readable because it describes intent rather than mechanism. A CONTROL FOOTING with SUM says "print a subtotal here" — it does not say "add this value to that accumulator, check if the key changed, if so format this line, write it, reset the accumulator, check the line count..."

The trade-off is flexibility. Declarative approaches work beautifully for common patterns but struggle with edge cases. This is why both Report Writer and manual report programming continue to coexist in the COBOL ecosystem — each has its place.


16.17 Complex Report Layouts: Cross-Footing and Computed Columns

Real-world reports frequently require computed values that go beyond simple SUM accumulation. Report Writer's SUM clause handles vertical accumulation (summing detail amounts into subtotals), but many reports also need horizontal calculations — cross-footing, percentages, and derived columns.

16.17.1 Cross-Footing Pattern

Cross-footing means verifying that row totals match column totals. In a manual report program, this is tedious bookkeeping. With Report Writer, you combine SUM for the vertical totals and USE BEFORE REPORTING for the horizontal:

       01  DETAIL-LINE TYPE IS DETAIL.
           05  LINE PLUS 1.
               10  RPT-DEPT        PIC X(10)
                   SOURCE IS WS-DEPT-NAME.
               10  RPT-REVENUE     COLUMN 15  PIC -(9)9.99
                   SOURCE IS WS-REVENUE.
               10  RPT-EXPENSES    COLUMN 30  PIC -(9)9.99
                   SOURCE IS WS-EXPENSES.
               10  RPT-NET         COLUMN 45  PIC -(9)9.99
                   SOURCE IS WS-NET-AMT.

      *--- In DECLARATIVES ---
       DETAIL-CALC SECTION.
           USE BEFORE REPORTING DETAIL-LINE.
       DETAIL-CALC-PARA.
           COMPUTE WS-NET-AMT =
               WS-REVENUE - WS-EXPENSES.
       END DECLARATIVES.

The USE BEFORE REPORTING declarative fires before each detail line is generated, allowing you to compute derived values that Report Writer cannot express declaratively. The revenue and expense columns are summed automatically by control footing SUM clauses, and the net column is also summed, providing both vertical and horizontal totals.

16.17.2 Percentage Columns in Control Footings

Averages and percentages cannot be expressed directly in SUM clauses. Use the declarative approach:

       01  TYPE IS CONTROL FOOTING REGION-CODE.
           05  LINE PLUS 1.
               10  COLUMN 5    PIC X(20)
                   VALUE 'REGION SUBTOTAL:'.
               10  COLUMN 30   PIC -(11)9.99
                   SUM RPT-REVENUE.
               10  COLUMN 50   PIC -(11)9.99
                   SUM RPT-EXPENSES.
               10  RPT-REG-NET COLUMN 70   PIC -(11)9.99
                   SUM RPT-NET.
               10  RPT-REG-PCT COLUMN 90   PIC ZZ9.9
                   SOURCE IS WS-REGION-PCT.
               10  COLUMN 96   PIC X(1)  VALUE '%'.

      *--- In DECLARATIVES ---
       REGION-FOOTING-CALC SECTION.
           USE BEFORE REPORTING REGION-CF-GROUP.
       REGION-FOOTING-CALC-PARA.
           IF WS-REGION-REVENUE NOT = ZERO
               COMPUTE WS-REGION-PCT =
                   (WS-REGION-EXPENSES /
                    WS-REGION-REVENUE) * 100
           ELSE
               MOVE ZERO TO WS-REGION-PCT
           END-IF.
       END DECLARATIVES.

The percentage column uses SOURCE to reference a WORKING-STORAGE field that is computed in the declarative. The SUM columns for revenue, expenses, and net are automatic; the percentage is computed procedurally.

16.17.3 Conditional Markers and Flagging

Some reports need to flag exceptional values — for example, marking accounts where the net activity exceeds a threshold:

       01  DETAIL-LINE TYPE IS DETAIL.
           05  LINE PLUS 1.
               10  COLUMN 1    PIC X(10)
                   SOURCE IS TXN-ACCT-NUM.
               10  COLUMN 15   PIC -(9)9.99
                   SOURCE IS TXN-AMOUNT.
               10  COLUMN 35   PIC X(5)
                   SOURCE IS WS-FLAG-MARKER.

      *--- In DECLARATIVES ---
       FLAG-CHECK SECTION.
           USE BEFORE REPORTING DETAIL-LINE.
       FLAG-CHECK-PARA.
           IF TXN-AMOUNT > 10000
               MOVE '** HI' TO WS-FLAG-MARKER
           ELSE
               MOVE SPACES TO WS-FLAG-MARKER
           END-IF.
       END DECLARATIVES.

This approach keeps the Report Writer declarations clean while adding conditional logic through the declarative mechanism.


16.18 Multi-Level Control Breaks: Three or More Levels

While two-level control breaks (account within branch) are common, production reports often require three or more levels. Report Writer handles any number of control levels, though careful design is needed to keep the report readable.

16.18.1 Four-Level Control Break Example

Consider a GlobalBank report sorted by Division, Region, Branch, and Account:

       RD  BRANCH-ACTIVITY-REPORT
           CONTROLS ARE FINAL
                       TXN-DIVISION
                       TXN-REGION
                       TXN-BRANCH
                       TXN-ACCT-NUM
           PAGE LIMIT IS 66 LINES
           HEADING 1
           FIRST DETAIL 8
           LAST DETAIL 56
           FOOTING 63.

The CONTROLS clause lists fields from major to minor. When TXN-DIVISION changes, all lower-level control footings fire first (account, branch, region), then the division footing, followed by the new division heading, new region heading, new branch heading, and so on.

16.18.2 Cascading SUM at Four Levels

      *--- Account-level subtotal ---
       01  TYPE IS CONTROL FOOTING TXN-ACCT-NUM.
           05  LINE PLUS 1.
               10  COLUMN 10  PIC X(18)
                   VALUE 'ACCOUNT SUBTOTAL:'.
               10  RPT-ACCT-SUM  COLUMN 50  PIC -(9)9.99
                   SUM RPT-DETAIL-AMT.

      *--- Branch-level subtotal ---
       01  TYPE IS CONTROL FOOTING TXN-BRANCH.
           05  LINE PLUS 1.
               10  COLUMN 5   PIC X(18)
                   VALUE 'BRANCH SUBTOTAL: '.
               10  COLUMN 50  PIC -(11)9.99
                   SUM RPT-DETAIL-AMT.

      *--- Region-level subtotal ---
       01  TYPE IS CONTROL FOOTING TXN-REGION.
           05  LINE PLUS 2.
               10  COLUMN 3   PIC X(18)
                   VALUE 'REGION SUBTOTAL: '.
               10  COLUMN 50  PIC -(13)9.99
                   SUM RPT-DETAIL-AMT.

      *--- Division-level subtotal ---
       01  TYPE IS CONTROL FOOTING TXN-DIVISION.
           05  LINE PLUS 2.
               10  COLUMN 1   PIC X(20)
                   VALUE 'DIVISION SUBTOTAL:  '.
               10  COLUMN 50  PIC -(15)9.99
                   SUM RPT-DETAIL-AMT.

      *--- Grand total ---
       01  TYPE IS CONTROL FOOTING FINAL.
           05  LINE PLUS 3.
               10  COLUMN 1   PIC X(20)
                   VALUE '*** GRAND TOTAL ***'.
               10  COLUMN 50  PIC -(15)9.99
                   SUM RPT-DETAIL-AMT.

Notice that each higher level uses a wider PIC clause (more digits) to accommodate the larger totals. The indentation of the VALUE labels (column positions 10, 5, 3, 1) creates a visual hierarchy on the printed report.

📊 Design Tip: For reports with more than three control levels, consider whether all levels are truly needed on a single report. Priya Kapoor observes: "When I see five control levels in one report, I ask whether the user really needs all that detail. Often, a summary report at the top two levels plus a drill-down detail report at the bottom two levels serves the business better."


16.19 Page Overflow Handling: Edge Cases

Report Writer's automatic page overflow handling works well in most cases, but certain edge cases require special attention.

16.19.1 Control Break at Page Boundary

When a control break occurs on the last detail line of a page, Report Writer must print the control footing on the current page, then start a new page with the page heading and control heading. This sequence is:

  1. Detect control break
  2. Print control footing(s) on current page (if they fit within FOOTING limit)
  3. If the footing exceeds the FOOTING limit, advance to new page first
  4. After footings, advance to new page
  5. Print page heading
  6. Print control heading(s)
  7. Resume with next detail line

The PAGE LIMIT parameters control this behavior:

FIRST DETAIL 8     — detail lines start at line 8
LAST DETAIL 54     — last detail line on the page
FOOTING 58         — footings can extend to line 58
PAGE LIMIT 66      — total lines per page

The gap between LAST DETAIL (54) and FOOTING (58) provides four lines for control footings to print even when the page is full of detail lines.

16.19.2 Long Control Footings

If a control footing requires more lines than the gap between LAST DETAIL and FOOTING allows, Report Writer forces a page break before printing the footing. This can result in a page with no detail lines — just the footing from the previous group.

To avoid this, ensure your FOOTING value provides enough room for the largest control footing group:

      *--- If your largest footing needs 6 lines ---
      *--- Set FOOTING at least 6 lines past LAST DETAIL ---
           PAGE LIMIT IS 66 LINES
           LAST DETAIL 48
           FOOTING 58          *> 10 lines for footings

16.19.3 Testing Page Overflow

Thorough testing of page overflow requires creating test data that triggers breaks at specific page positions. Derek Washington creates test files with record counts calculated to force breaks at boundary conditions:

Test case 1: Page break with no pending control break
Test case 2: Control break on exact LAST DETAIL line
Test case 3: Control break where footing exceeds FOOTING limit
Test case 4: Multiple nested control breaks at page boundary
Test case 5: First record on new page triggers immediate break

🧪 Try It Yourself: Create a Report Writer program with LAST DETAIL 20 and FOOTING 25 (a small page for easy testing). Create test data with exactly 20 detail records per control group. Verify that page overflow and control breaks interact correctly. Then reduce to 18 detail records per group and observe the difference in footing placement.


16.20 The SUPPRESS Statement

Report Writer provides the SUPPRESS statement within USE BEFORE REPORTING declaratives to prevent a report group from being printed:

       DECLARATIVES.
       SUPPRESS-ZERO-ACCOUNTS SECTION.
           USE BEFORE REPORTING ACCT-DETAIL-LINE.
       SUPPRESS-ZERO-PARA.
           IF WS-ACCT-BALANCE = ZERO
               MOVE 'Y' TO WS-SUPPRESSED
               SUPPRESS PRINTING
           END-IF.
       END DECLARATIVES.

When SUPPRESS PRINTING is executed, the current report group is not written to the output. However, SUM accumulation still occurs — the suppressed detail's values are still included in control footing totals. This is useful for producing summary reports where you want subtotals and grand totals but want to suppress certain detail lines (e.g., zero-balance accounts).

⚠️ SUM Still Counts: Suppressed detail lines still contribute to SUM fields. If you need to exclude suppressed lines from totals, you must adjust the totals manually in USE BEFORE REPORTING for the control footing.


16.21 MedClaim Case Study: Provider Activity Report with Three Control Levels

Let us build one more example that demonstrates a real-world three-level control break with computed fields: MedClaim's monthly provider activity report, grouped by region, provider, and claim type.

16.21.1 Requirements

Sarah Kim needs a report showing: - Claims grouped by region, then provider, then claim type (Medical/Dental/Pharmacy) - For each claim type: subtotal of charges and count of claims - For each provider: total charges, total claims, and average charge - For each region: total charges, total claims - Grand total across all regions - Average charge per claim at provider and grand total levels

16.21.2 The Average Charge Challenge

Report Writer's SUM clause handles totaling, but average charge requires division — which SUM cannot express. The solution combines SUM for the totals with USE BEFORE REPORTING for the averages:

       WORKING-STORAGE SECTION.
       01  WS-PROVIDER-AVG       PIC -(7)9.99.
       01  WS-GRAND-AVG          PIC -(7)9.99.
       01  WS-PROV-SUM-HOLD      PIC S9(11)V99 COMP-3.
       01  WS-PROV-CNT-HOLD      PIC 9(07)     COMP.
       01  WS-GRAND-SUM-HOLD     PIC S9(13)V99 COMP-3.
       01  WS-GRAND-CNT-HOLD     PIC 9(09)     COMP.

       REPORT SECTION.
       RD  PROVIDER-ACTIVITY-RPT
           CONTROLS ARE FINAL
                       PA-REGION
                       PA-PROVIDER
                       PA-CLAIM-TYPE
           PAGE LIMIT IS 66 LINES
           HEADING 1
           FIRST DETAIL 8
           LAST DETAIL 54
           FOOTING 62.

      *--- DETAIL LINE ---
       01  PA-DETAIL TYPE IS DETAIL.
           05  LINE PLUS 1.
               10  PA-DET-COUNTER  PIC 9 VALUE 1.
               10  COLUMN 1    PIC X(04)
                   SOURCE IS PA-REGION
                   GROUP INDICATE.
               10  COLUMN 8    PIC X(10)
                   SOURCE IS PA-PROVIDER
                   GROUP INDICATE.
               10  COLUMN 20   PIC X(02)
                   SOURCE IS PA-CLAIM-TYPE
                   GROUP INDICATE.
               10  COLUMN 26   PIC 9999/99/99
                   SOURCE IS PA-CLAIM-DATE.
               10  COLUMN 40   PIC X(15)
                   SOURCE IS PA-CLAIM-ID.
               10  PA-DET-AMT
                   COLUMN 60   PIC -(7)9.99
                   SOURCE IS PA-CHARGE-AMT.

      *--- Claim Type subtotal ---
       01  TYPE IS CONTROL FOOTING PA-CLAIM-TYPE.
           05  LINE PLUS 1.
               10  COLUMN 20   PIC X(25)
                   VALUE '  TYPE SUBTOTAL:'.
               10  COLUMN 60   PIC -(9)9.99
                   SUM PA-DET-AMT.
               10  COLUMN 80   PIC X(08)  VALUE 'CLAIMS: '.
               10  COLUMN 88   PIC Z(5)9
                   SUM PA-DET-COUNTER.

      *--- Provider subtotal with average ---
       01  PA-PROV-CF TYPE IS CONTROL FOOTING PA-PROVIDER.
           05  LINE PLUS 1.
               10  COLUMN 8    PIC X(90) VALUE ALL '-'.
           05  LINE PLUS 1.
               10  COLUMN 8    PIC X(18)
                   VALUE 'PROVIDER TOTAL:'.
               10  PA-PROV-SUM
                   COLUMN 60   PIC -(11)9.99
                   SUM PA-DET-AMT.
               10  COLUMN 80   PIC X(08)  VALUE 'CLAIMS: '.
               10  PA-PROV-CNT
                   COLUMN 88   PIC Z(7)9
                   SUM PA-DET-COUNTER.
           05  LINE PLUS 1.
               10  COLUMN 8    PIC X(18)
                   VALUE 'AVERAGE CHARGE:'.
               10  COLUMN 60   PIC -(7)9.99
                   SOURCE IS WS-PROVIDER-AVG.

      *--- Grand total with average ---
       01  PA-GRAND-CF TYPE IS CONTROL FOOTING FINAL.
           05  LINE PLUS 2.
               10  COLUMN 1    PIC X(120) VALUE ALL '='.
           05  LINE PLUS 1.
               10  COLUMN 1    PIC X(22)
                   VALUE '*** GRAND TOTAL: ***'.
               10  PA-GRAND-SUM
                   COLUMN 60   PIC -(13)9.99
                   SUM PA-DET-AMT.
               10  COLUMN 80   PIC X(08)  VALUE 'CLAIMS: '.
               10  PA-GRAND-CNT
                   COLUMN 88   PIC Z(9)9
                   SUM PA-DET-COUNTER.
           05  LINE PLUS 1.
               10  COLUMN 1    PIC X(22)
                   VALUE 'AVERAGE CHARGE:     '.
               10  COLUMN 60   PIC -(7)9.99
                   SOURCE IS WS-GRAND-AVG.

       DECLARATIVES.
       PROV-CF-SECTION SECTION.
           USE BEFORE REPORTING PA-PROV-CF.
       PROV-CF-PARA.
           IF WS-PROV-CNT-HOLD > 0
               COMPUTE WS-PROVIDER-AVG ROUNDED =
                   WS-PROV-SUM-HOLD / WS-PROV-CNT-HOLD
           ELSE
               MOVE ZERO TO WS-PROVIDER-AVG
           END-IF.

       GRAND-CF-SECTION SECTION.
           USE BEFORE REPORTING PA-GRAND-CF.
       GRAND-CF-PARA.
           IF WS-GRAND-CNT-HOLD > 0
               COMPUTE WS-GRAND-AVG ROUNDED =
                   WS-GRAND-SUM-HOLD / WS-GRAND-CNT-HOLD
           ELSE
               MOVE ZERO TO WS-GRAND-AVG
           END-IF.
       END DECLARATIVES.

⚠️ Timing of USE BEFORE REPORTING: The declarative fires before the report group is generated — but after the SUM fields have been accumulated. This means you can reference the SUM values (via WORKING-STORAGE fields that you maintain in parallel, since direct access to SUM counters from declaratives requires careful coordination) to compute averages and percentages.

16.21.3 Maintaining Parallel Accumulators

The challenge with the above approach is that SUM fields in Report Writer are managed internally. To access their values in a declarative, you must maintain parallel accumulators in WORKING-STORAGE:

       PROCEDURE DIVISION.
       1000-PROCESS-CLAIMS.
           ADD PA-CHARGE-AMT TO WS-PROV-SUM-HOLD
           ADD PA-CHARGE-AMT TO WS-GRAND-SUM-HOLD
           ADD 1 TO WS-PROV-CNT-HOLD
           ADD 1 TO WS-GRAND-CNT-HOLD
           GENERATE PA-DETAIL
           READ CLAIM-FILE
               AT END SET END-OF-FILE TO TRUE
           END-READ.

And reset the provider accumulators when the provider control break occurs (in the provider control heading declarative or at the start of each new provider group). This parallel-accumulator pattern is the standard workaround for Report Writer's inability to expose SUM values to procedural code.

📊 Trade-off Analysis: This dual-accumulator approach partly negates Report Writer's advantage of automatic accumulation. For reports with complex computed columns, the manual overhead of maintaining parallel accumulators may argue in favor of a fully manual report program. Maria Chen's guidance: "If more than half your control footing fields require USE BEFORE REPORTING computation, you have probably outgrown Report Writer for that particular report."


16.22 Chapter Summary

In this chapter, we explored the COBOL Report Writer facility — a declarative approach to report generation that has been part of the COBOL standard since 1960. We covered:

  • Report Writer architecture: RD entries, report groups (RH, PH, CH, DE, CF, PF, RF), and the three PROCEDURE DIVISION statements (INITIATE, GENERATE, TERMINATE)
  • Key clauses: LINE, COLUMN, SOURCE, VALUE, SUM, GROUP INDICATE
  • Control break handling: Automatic break detection via CONTROLS, with cascading SUM accumulation
  • Page management: Automatic page overflow detection, header/footer generation
  • GENERATE statement: Detail reporting vs. summary reporting
  • Manual control break processing: The procedural alternative, with its trade-offs in complexity and flexibility
  • Comparison: When to use Report Writer vs. manual report generation
  • Production considerations: Print control characters, testing strategies, performance

Report Writer exemplifies the theme of Readability is a Feature. By moving report structure from procedural code to declarative descriptions, Report Writer makes reports easier to understand, modify, and maintain. The PROCEDURE DIVISION focuses on data processing; the REPORT SECTION describes the report. Each part does what it does best.

In the next chapter, we turn to string handling — the INSPECT, STRING, UNSTRING, and reference modification features that make COBOL a surprisingly capable text processing language.


Key Terms Introduced in This Chapter

Term Definition
RD (Report Description) A declaration defining a report's physical layout and control hierarchy
Report group A unit of report output (heading, detail, footing, etc.)
INITIATE Statement that starts a report, initializing all accumulators
GENERATE Statement that produces report output and triggers automatic processing
TERMINATE Statement that ends a report, producing final footings
CONTROLS RD clause listing the fields that trigger control breaks
SUM Report group clause that automatically accumulates values
GROUP INDICATE Clause that prints a field only on the first line of each group
SOURCE Clause naming the data item whose value appears in the report
PAGE-COUNTER Special register tracking the current page number
LINE-COUNTER Special register tracking the current line position
Control break An event triggered when a control field's value changes
USE BEFORE REPORTING Declarative that executes code before a report group is generated