MODULE 10 · BATCH REPORTS · 5/7

Control breaks and subtotals

15 min30 XPExercise

A control break is the moment, while reading sorted input, when the value of a key field (the control field) changes: the last order for customer C1 has gone by and the first for C2 has arrived. At that moment you print C1's subtotal, add it to the grand total, and start counting again for C2.

It is the single most common pattern in batch reporting, and it only works because the input is sorted on the control field. That's why the SORT step so often comes right before the report step in a job.

The algorithm

  1. Read the first record; remember its control field as WS-PREV-....
  2. For each record: if its control field differs from the previous one, break first: print the previous group's total, roll it into the grand total, reset it, remember the new key.
  3. Then process the record: print the detail, add to the group total.
  4. At end of file, do one final break for the last group, then print the grand total.
       IDENTIFICATION DIVISION.
       PROGRAM-ID. BREAKDEMO.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 ORDER-DATA.
          05 FILLER       PIC X(7) VALUE "C1 0010".
          05 FILLER       PIC X(7) VALUE "C1 0025".
          05 FILLER       PIC X(7) VALUE "C2 0100".
          05 FILLER       PIC X(7) VALUE "C3 0005".
          05 FILLER       PIC X(7) VALUE "C3 0005".
       01 ORDER-TABLE REDEFINES ORDER-DATA.
          05 ORDER-ENTRY OCCURS 5 TIMES.
             10 ORD-CUST  PIC X(2).
             10 FILLER    PIC X.
             10 ORD-QTY   PIC 9(4).
       01 WS-I            PIC 9.
       01 WS-PREV-CUST    PIC X(2).
       01 WS-CUST-TOTAL   PIC 9(5) VALUE 0.
       01 WS-GRAND-TOTAL  PIC 9(5) VALUE 0.
       01 WS-QTY-ED       PIC ZZ,ZZ9.
       PROCEDURE DIVISION.
           MOVE ORD-CUST(1) TO WS-PREV-CUST
           PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > 5
               IF ORD-CUST(WS-I) NOT = WS-PREV-CUST
                   PERFORM CUSTOMER-BREAK
               END-IF
               MOVE ORD-QTY(WS-I) TO WS-QTY-ED
               DISPLAY ORD-CUST(WS-I) "   " WS-QTY-ED
               ADD ORD-QTY(WS-I) TO WS-CUST-TOTAL
           END-PERFORM
           PERFORM CUSTOMER-BREAK
           MOVE WS-GRAND-TOTAL TO WS-QTY-ED
           DISPLAY "ALL  " WS-QTY-ED " **"
           STOP RUN.

       CUSTOMER-BREAK.
           MOVE WS-CUST-TOTAL TO WS-QTY-ED
           DISPLAY "     " WS-QTY-ED " * " WS-PREV-CUST " TOTAL"
           ADD WS-CUST-TOTAL TO WS-GRAND-TOTAL
           MOVE 0 TO WS-CUST-TOTAL
           IF WS-I <= 5
               MOVE ORD-CUST(WS-I) TO WS-PREV-CUST
           END-IF.

Output:

C1       10
C1       25
         35 * C1 TOTAL
C2      100
        100 * C2 TOTAL
C3        5
C3        5
         10 * C3 TOTAL
ALL     145 **

(The input lives in a table here so the example runs on its own. In a real job it comes from a sorted file, read one record at a time.)

Details that matter

  • Label the total with the previous key. When the break fires, the record you just read already belongs to the next group. Print WS-PREV-CUST, not the field in the record.
  • End of file is a break. Forgetting the final break is the classic bug: every group gets its total except the last.
  • Empty input. If there were no records there is no group to close. Guard the final break with a record count or a first-record flag, or the report shows a total for a blank key.
  • Roll totals upward, then reset. Add the group total into the grand total at the break instead of adding every detail to both. With more levels (next lesson) each level rolls into the one above.
  • Asterisks mark levels. One * on a group total, two on the grand total: an old print-shop convention you will still see in many reports.

On the job

If a control-break report suddenly shows the same group twice, with two partial subtotals, don't debug the COBOL first. Check the sort step: the input probably wasn't sorted on the control field (a changed SORT FIELDS, or a new file concatenated after the sort).

Your task

Sales wants totals per region. The input is already sorted by region.

DD SALESIN — line sequential, 22 bytes: SALE-REGION X(5), SALE-REP X(10), SALE-AMOUNT 9(5)V99.

DD RPTOUT — the report. The starter has all the layouts, the headings and the detail printing.

Add the control break: when the region changes, and after the last record, print that region's total line followed by a blank line. At the very end print the grand total.

SALES BY REGION
REGION REP                AMOUNT
EAST   MURPHY          12,999.99
EAST   DOYLE              410.00
  EAST  TOTAL          13,409.99

NORTH  WALSH            8,150.00
...
  SOUTH TOTAL           5,125.50

GRAND TOTAL            49,070.49

An empty input file prints the two heading lines and GRAND TOTAL 0.00 (with the usual spacing), and no region total.

fixed format
Run your program to see its output here. The first visible test's input and datasets are used.
Submit to grade your program against every test.