Case Study 1: Daily Transaction Processing at Pioneer National Bank

Background

Pioneer National Bank processes all customer transactions through a nightly batch cycle. During business hours, transactions from ATMs, teller stations, online banking, and mobile apps are collected into a sequential transaction file. Each night, the Daily Transaction Processing program (DLYTRXN) reads this file, validates each transaction, accumulates summary totals by transaction type, and produces a Daily Transaction Summary Report for the operations team.

This batch job is the first step in Pioneer National's end-of-day processing chain. If DLYTRXN fails or produces incorrect totals, every downstream job -- account posting, interest calculation, statement generation, and regulatory reporting -- is affected. The program must therefore be production-hardened with comprehensive file status checking, error counting, and control total verification.

Marcus Williams, a COBOL developer with 15 years of mainframe experience, designed DLYTRXN to demonstrate best practices in sequential file processing: rigorous file status checking on every I/O operation, clean EOF handling, error accumulation with threshold-based abort logic, and control total reconciliation.


The Problem

The daily transaction file (TRANFILE) contains between 50,000 and 500,000 records depending on business volume. Each record is an 80-byte fixed-length record containing:

  • Transaction ID (12 digits)
  • Account Number (10 digits)
  • Transaction Type (2-character code: DP=deposit, WD=withdrawal, TR=transfer, FE=fee, IN=interest, AD=adjustment)
  • Transaction Amount (signed, up to $9,999,999.99)
  • Transaction Date (YYYYMMDD)
  • Transaction Time (HHMMSS)
  • Branch Code (4 characters)
  • Teller ID (6 characters)
  • Filler (18 characters)

The program must produce:

  1. Summary Report -- Total count and total amount for each transaction type
  2. Error Report -- All records that fail validation, with reason codes
  3. Control Totals -- Grand total count, debit total, and credit total for reconciliation
  4. An exit with return code 0 if processing completes normally, return code 4 if errors were found but within threshold (under 1% of records), and return code 8 if errors exceed the threshold.

The Solution

The COBOL Program

       IDENTIFICATION DIVISION.
       PROGRAM-ID.  DLYTRXN.
       AUTHOR.      MARCUS WILLIAMS.
       DATE-WRITTEN. 2024-11-15.
      *================================================================
      * PROGRAM:  DLYTRXN - DAILY TRANSACTION PROCESSING
      * PURPOSE:  Read the daily transaction file, validate
      *           each record, accumulate summary totals by
      *           transaction type, and produce a summary report
      *           and error report. Demonstrates sequential file
      *           processing with rigorous file status checking,
      *           EOF handling, and control total reconciliation.
      *================================================================

       ENVIRONMENT DIVISION.

       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT TRANSACTION-FILE
               ASSIGN TO TRANFILE
               ORGANIZATION IS SEQUENTIAL
               ACCESS MODE IS SEQUENTIAL
               FILE STATUS IS WS-TRAN-STATUS.

           SELECT SUMMARY-REPORT
               ASSIGN TO SUMRPT
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-SUMR-STATUS.

           SELECT ERROR-REPORT
               ASSIGN TO ERRRPT
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-ERRR-STATUS.

       DATA DIVISION.

       FILE SECTION.

       FD  TRANSACTION-FILE
           RECORDING MODE IS F
           RECORD CONTAINS 80 CHARACTERS
           BLOCK CONTAINS 0 RECORDS.
       01  FS-TRAN-RECORD.
           05  FS-TR-TRANS-ID         PIC 9(12).
           05  FS-TR-ACCOUNT-NO       PIC 9(10).
           05  FS-TR-TYPE             PIC X(2).
           05  FS-TR-AMOUNT           PIC S9(7)V99.
           05  FS-TR-DATE.
               10  FS-TR-YEAR        PIC 9(4).
               10  FS-TR-MONTH       PIC 9(2).
               10  FS-TR-DAY         PIC 9(2).
           05  FS-TR-TIME.
               10  FS-TR-HOUR        PIC 9(2).
               10  FS-TR-MINUTE      PIC 9(2).
               10  FS-TR-SECOND      PIC 9(2).
           05  FS-TR-BRANCH          PIC X(4).
           05  FS-TR-TELLER          PIC X(6).
           05  FILLER                PIC X(18).

       FD  SUMMARY-REPORT
           RECORDING MODE IS F
           RECORD CONTAINS 132 CHARACTERS.
       01  FS-SUM-LINE               PIC X(132).

       FD  ERROR-REPORT
           RECORDING MODE IS F
           RECORD CONTAINS 132 CHARACTERS.
       01  FS-ERR-LINE               PIC X(132).

       WORKING-STORAGE SECTION.

      *----------------------------------------------------------------
      * FILE STATUS FIELDS
      * Every file gets its own status field. Every I/O
      * operation checks its status field immediately after.
      *----------------------------------------------------------------
       01  WS-TRAN-STATUS            PIC X(2).
           88  TRAN-OK                           VALUE "00".
           88  TRAN-EOF                          VALUE "10".
           88  TRAN-ERROR                        VALUE "30" THRU
                                                       "99".
       01  WS-SUMR-STATUS            PIC X(2).
           88  SUMR-OK                           VALUE "00".
       01  WS-ERRR-STATUS            PIC X(2).
           88  ERRR-OK                           VALUE "00".

      *----------------------------------------------------------------
      * PROCESSING FLAGS
      *----------------------------------------------------------------
       01  WS-EOF-FLAG               PIC X(1) VALUE 'N'.
           88  END-OF-FILE                       VALUE 'Y'.
           88  NOT-END-OF-FILE                   VALUE 'N'.
       01  WS-ABORT-FLAG             PIC X(1) VALUE 'N'.
           88  ABORT-PROCESSING                  VALUE 'Y'.
           88  CONTINUE-PROCESSING               VALUE 'N'.

      *----------------------------------------------------------------
      * VALIDATION FLAGS
      *----------------------------------------------------------------
       01  WS-VALID-RECORD           PIC X(1).
           88  RECORD-VALID                      VALUE 'Y'.
           88  RECORD-INVALID                    VALUE 'N'.
       01  WS-ERROR-REASON           PIC X(30).

      *----------------------------------------------------------------
      * VALID TRANSACTION TYPES
      *----------------------------------------------------------------
       01  WS-VALID-TYPES.
           05  FILLER PIC X(2) VALUE "DP".
           05  FILLER PIC X(2) VALUE "WD".
           05  FILLER PIC X(2) VALUE "TR".
           05  FILLER PIC X(2) VALUE "FE".
           05  FILLER PIC X(2) VALUE "IN".
           05  FILLER PIC X(2) VALUE "AD".
       01  WS-VALID-TYPE-TABLE REDEFINES WS-VALID-TYPES.
           05  WS-VALID-TYPE          PIC X(2)
                                      OCCURS 6 TIMES.
       01  WS-TYPE-IDX               PIC 9(1).
       01  WS-TYPE-FOUND             PIC X(1).

      *----------------------------------------------------------------
      * SUMMARY ACCUMULATORS (by transaction type)
      * Index: 1=DP, 2=WD, 3=TR, 4=FE, 5=IN, 6=AD
      *----------------------------------------------------------------
       01  WS-SUMMARY-TABLE.
           05  WS-SUM-ENTRY          OCCURS 6 TIMES.
               10  WS-SUM-TYPE-CODE  PIC X(2).
               10  WS-SUM-TYPE-DESC  PIC X(15).
               10  WS-SUM-COUNT      PIC S9(7) COMP-3.
               10  WS-SUM-DEBIT-TOT  PIC S9(11)V99 COMP-3.
               10  WS-SUM-CREDIT-TOT PIC S9(11)V99 COMP-3.

      *----------------------------------------------------------------
      * GRAND TOTALS
      *----------------------------------------------------------------
       01  WS-GRAND-TOTALS.
           05  WS-TOTAL-RECORDS      PIC S9(7) COMP-3 VALUE 0.
           05  WS-TOTAL-VALID        PIC S9(7) COMP-3 VALUE 0.
           05  WS-TOTAL-ERRORS       PIC S9(7) COMP-3 VALUE 0.
           05  WS-GRAND-DEBITS       PIC S9(13)V99 COMP-3
                                     VALUE ZERO.
           05  WS-GRAND-CREDITS      PIC S9(13)V99 COMP-3
                                     VALUE ZERO.

      *----------------------------------------------------------------
      * ERROR THRESHOLD
      *----------------------------------------------------------------
       01  WS-ERROR-THRESHOLD        PIC 9V99 VALUE 0.01.
       01  WS-ERROR-RATE             PIC 9V9(4) COMP-3.

      *----------------------------------------------------------------
      * RETURN CODE
      *----------------------------------------------------------------
       01  WS-RETURN-CODE            PIC S9(4) COMP VALUE 0.

      *----------------------------------------------------------------
      * REPORT LINES
      *----------------------------------------------------------------
       01  WS-RPT-HEADER-1.
           05  FILLER PIC X(50) VALUE
               "PIONEER NATIONAL BANK - DAILY TRANSACTION SUMMARY".
           05  FILLER PIC X(20) VALUE SPACES.
           05  FILLER PIC X(6) VALUE "DATE: ".
           05  WS-HDR-DATE PIC X(10).
           05  FILLER PIC X(46) VALUE SPACES.

       01  WS-RPT-COL-HDR.
           05  FILLER PIC X(6) VALUE "TYPE  ".
           05  FILLER PIC X(17) VALUE "DESCRIPTION      ".
           05  FILLER PIC X(12) VALUE "       COUNT".
           05  FILLER PIC X(20) VALUE "       DEBIT TOTAL  ".
           05  FILLER PIC X(20) VALUE "      CREDIT TOTAL  ".
           05  FILLER PIC X(57) VALUE SPACES.

       01  WS-RPT-DETAIL.
           05  WS-DTL-TYPE           PIC X(4).
           05  FILLER                PIC X(2) VALUE SPACES.
           05  WS-DTL-DESC           PIC X(15).
           05  FILLER                PIC X(2) VALUE SPACES.
           05  WS-DTL-COUNT          PIC ZZ,ZZZ,ZZ9.
           05  FILLER                PIC X(2) VALUE SPACES.
           05  WS-DTL-DEBITS         PIC $Z,ZZZ,ZZZ,ZZ9.99-.
           05  FILLER                PIC X(2) VALUE SPACES.
           05  WS-DTL-CREDITS        PIC $Z,ZZZ,ZZZ,ZZ9.99-.
           05  FILLER                PIC X(57) VALUE SPACES.

       01  WS-RPT-TOTAL-LINE.
           05  FILLER PIC X(6) VALUE "TOTALS".
           05  FILLER PIC X(17) VALUE SPACES.
           05  WS-TOT-COUNT          PIC ZZ,ZZZ,ZZ9.
           05  FILLER                PIC X(2) VALUE SPACES.
           05  WS-TOT-DEBITS         PIC $Z,ZZZ,ZZZ,ZZ9.99-.
           05  FILLER                PIC X(2) VALUE SPACES.
           05  WS-TOT-CREDITS        PIC $Z,ZZZ,ZZZ,ZZ9.99-.
           05  FILLER                PIC X(57) VALUE SPACES.

       01  WS-ERR-DETAIL.
           05  WS-ERR-TRANS-ID       PIC 9(12).
           05  FILLER                PIC X(1) VALUE SPACE.
           05  WS-ERR-ACCOUNT        PIC 9(10).
           05  FILLER                PIC X(1) VALUE SPACE.
           05  WS-ERR-TYPE           PIC X(2).
           05  FILLER                PIC X(1) VALUE SPACE.
           05  WS-ERR-AMOUNT         PIC -(7)9.99.
           05  FILLER                PIC X(1) VALUE SPACE.
           05  WS-ERR-REASON         PIC X(30).
           05  FILLER                PIC X(55) VALUE SPACES.

       01  WS-DISP-COUNT            PIC Z,ZZZ,ZZ9.

       PROCEDURE DIVISION.

       0000-MAIN-CONTROL.
           PERFORM 1000-INITIALIZE
           PERFORM 2000-PROCESS-TRANSACTIONS
               UNTIL END-OF-FILE OR ABORT-PROCESSING
           PERFORM 3000-WRITE-SUMMARY-REPORT
           PERFORM 4000-RECONCILE-TOTALS
           PERFORM 9000-FINALIZE
           MOVE WS-RETURN-CODE TO RETURN-CODE
           STOP RUN
           .

       1000-INITIALIZE.
           DISPLAY "DLYTRXN: Beginning daily transaction "
                   "processing"

      *    Initialize summary table
           MOVE "DP" TO WS-SUM-TYPE-CODE(1)
           MOVE "DEPOSITS"       TO WS-SUM-TYPE-DESC(1)
           MOVE "WD" TO WS-SUM-TYPE-CODE(2)
           MOVE "WITHDRAWALS"    TO WS-SUM-TYPE-DESC(2)
           MOVE "TR" TO WS-SUM-TYPE-CODE(3)
           MOVE "TRANSFERS"      TO WS-SUM-TYPE-DESC(3)
           MOVE "FE" TO WS-SUM-TYPE-CODE(4)
           MOVE "FEES"           TO WS-SUM-TYPE-DESC(4)
           MOVE "IN" TO WS-SUM-TYPE-CODE(5)
           MOVE "INTEREST"       TO WS-SUM-TYPE-DESC(5)
           MOVE "AD" TO WS-SUM-TYPE-CODE(6)
           MOVE "ADJUSTMENTS"    TO WS-SUM-TYPE-DESC(6)

           PERFORM VARYING WS-TYPE-IDX FROM 1 BY 1
               UNTIL WS-TYPE-IDX > 6
               MOVE ZERO TO WS-SUM-COUNT(WS-TYPE-IDX)
               MOVE ZERO TO WS-SUM-DEBIT-TOT(WS-TYPE-IDX)
               MOVE ZERO TO WS-SUM-CREDIT-TOT(WS-TYPE-IDX)
           END-PERFORM

      *    Open all files with status checking
           OPEN INPUT TRANSACTION-FILE
           IF NOT TRAN-OK
               DISPLAY "DLYTRXN: FATAL - Cannot open "
                       "TRANFILE. Status: " WS-TRAN-STATUS
               MOVE 16 TO WS-RETURN-CODE
               SET ABORT-PROCESSING TO TRUE
               STOP RUN
           END-IF

           OPEN OUTPUT SUMMARY-REPORT
           IF NOT SUMR-OK
               DISPLAY "DLYTRXN: FATAL - Cannot open "
                       "SUMRPT. Status: " WS-SUMR-STATUS
               MOVE 16 TO WS-RETURN-CODE
               SET ABORT-PROCESSING TO TRUE
               STOP RUN
           END-IF

           OPEN OUTPUT ERROR-REPORT
           IF NOT ERRR-OK
               DISPLAY "DLYTRXN: FATAL - Cannot open "
                       "ERRRPT. Status: " WS-ERRR-STATUS
               MOVE 16 TO WS-RETURN-CODE
               SET ABORT-PROCESSING TO TRUE
               STOP RUN
           END-IF

      *    Prime the read
           PERFORM 2100-READ-TRANSACTION
           .

       2000-PROCESS-TRANSACTIONS.
           ADD 1 TO WS-TOTAL-RECORDS

      *    Validate the transaction
           PERFORM 2200-VALIDATE-TRANSACTION

           IF RECORD-VALID
               PERFORM 2300-ACCUMULATE-TOTALS
               ADD 1 TO WS-TOTAL-VALID
           ELSE
               PERFORM 2400-WRITE-ERROR
               ADD 1 TO WS-TOTAL-ERRORS
      *        Check error threshold
               IF WS-TOTAL-RECORDS > 100
                   COMPUTE WS-ERROR-RATE =
                       WS-TOTAL-ERRORS / WS-TOTAL-RECORDS
                   IF WS-ERROR-RATE > WS-ERROR-THRESHOLD
                       DISPLAY "DLYTRXN: ERROR threshold "
                               "exceeded. Aborting."
                       DISPLAY "  Error rate: " WS-ERROR-RATE
                       MOVE 12 TO WS-RETURN-CODE
                       SET ABORT-PROCESSING TO TRUE
                   END-IF
               END-IF
           END-IF

      *    Read next record
           PERFORM 2100-READ-TRANSACTION
           .

       2100-READ-TRANSACTION.
           READ TRANSACTION-FILE
               AT END
                   SET END-OF-FILE TO TRUE
               NOT AT END
                   IF NOT TRAN-OK
                       DISPLAY "DLYTRXN: READ error on "
                               "TRANFILE. Status: "
                               WS-TRAN-STATUS
                       DISPLAY "  Record: " WS-TOTAL-RECORDS
                       ADD 1 TO WS-TOTAL-ERRORS
                   END-IF
           END-READ
           .

       2200-VALIDATE-TRANSACTION.
           SET RECORD-VALID TO TRUE

      *    Check 1: Transaction type must be valid
           MOVE 'N' TO WS-TYPE-FOUND
           PERFORM VARYING WS-TYPE-IDX FROM 1 BY 1
               UNTIL WS-TYPE-IDX > 6
               IF FS-TR-TYPE = WS-VALID-TYPE(WS-TYPE-IDX)
                   MOVE 'Y' TO WS-TYPE-FOUND
               END-IF
           END-PERFORM
           IF WS-TYPE-FOUND = 'N'
               SET RECORD-INVALID TO TRUE
               MOVE "INVALID TRANSACTION TYPE"
                   TO WS-ERROR-REASON
           END-IF

      *    Check 2: Amount must not be zero
           IF RECORD-VALID
               IF FS-TR-AMOUNT = ZERO
                   SET RECORD-INVALID TO TRUE
                   MOVE "ZERO AMOUNT NOT ALLOWED"
                       TO WS-ERROR-REASON
               END-IF
           END-IF

      *    Check 3: Date must be valid
           IF RECORD-VALID
               IF FS-TR-MONTH < 01 OR FS-TR-MONTH > 12
                   SET RECORD-INVALID TO TRUE
                   MOVE "INVALID MONTH IN DATE"
                       TO WS-ERROR-REASON
               END-IF
           END-IF

           IF RECORD-VALID
               IF FS-TR-DAY < 01 OR FS-TR-DAY > 31
                   SET RECORD-INVALID TO TRUE
                   MOVE "INVALID DAY IN DATE"
                       TO WS-ERROR-REASON
               END-IF
           END-IF

      *    Check 4: Account number must not be all zeros
           IF RECORD-VALID
               IF FS-TR-ACCOUNT-NO = ZERO
                   SET RECORD-INVALID TO TRUE
                   MOVE "INVALID ACCOUNT NUMBER"
                       TO WS-ERROR-REASON
               END-IF
           END-IF
           .

       2300-ACCUMULATE-TOTALS.
      *    -------------------------------------------------------
      *    Find the matching summary entry and add the amount
      *    to the appropriate total (debit or credit).
      *    -------------------------------------------------------
           PERFORM VARYING WS-TYPE-IDX FROM 1 BY 1
               UNTIL WS-TYPE-IDX > 6
               IF FS-TR-TYPE = WS-SUM-TYPE-CODE(WS-TYPE-IDX)
                   ADD 1 TO WS-SUM-COUNT(WS-TYPE-IDX)
                   IF FS-TR-AMOUNT < ZERO
                       ADD FS-TR-AMOUNT TO
                           WS-SUM-DEBIT-TOT(WS-TYPE-IDX)
                       ADD FS-TR-AMOUNT TO WS-GRAND-DEBITS
                   ELSE
                       ADD FS-TR-AMOUNT TO
                           WS-SUM-CREDIT-TOT(WS-TYPE-IDX)
                       ADD FS-TR-AMOUNT TO WS-GRAND-CREDITS
                   END-IF
               END-IF
           END-PERFORM
           .

       2400-WRITE-ERROR.
           INITIALIZE WS-ERR-DETAIL
           MOVE FS-TR-TRANS-ID    TO WS-ERR-TRANS-ID
           MOVE FS-TR-ACCOUNT-NO  TO WS-ERR-ACCOUNT
           MOVE FS-TR-TYPE        TO WS-ERR-TYPE
           MOVE FS-TR-AMOUNT      TO WS-ERR-AMOUNT
           MOVE WS-ERROR-REASON   TO WS-ERR-REASON
           MOVE WS-ERR-DETAIL TO FS-ERR-LINE
           WRITE FS-ERR-LINE
           IF NOT ERRR-OK
               DISPLAY "DLYTRXN: WRITE error on ERRRPT. "
                       "Status: " WS-ERRR-STATUS
           END-IF
           .

       3000-WRITE-SUMMARY-REPORT.
      *    -------------------------------------------------------
      *    Write the summary report with headers, detail lines
      *    for each transaction type, and grand totals.
      *    -------------------------------------------------------
      *    Header
           MOVE WS-RPT-HEADER-1 TO FS-SUM-LINE
           WRITE FS-SUM-LINE
           MOVE SPACES TO FS-SUM-LINE
           WRITE FS-SUM-LINE
           MOVE WS-RPT-COL-HDR TO FS-SUM-LINE
           WRITE FS-SUM-LINE
           MOVE ALL "-" TO FS-SUM-LINE
           WRITE FS-SUM-LINE

      *    Detail lines for each type
           PERFORM VARYING WS-TYPE-IDX FROM 1 BY 1
               UNTIL WS-TYPE-IDX > 6
               INITIALIZE WS-RPT-DETAIL
               MOVE WS-SUM-TYPE-CODE(WS-TYPE-IDX)
                   TO WS-DTL-TYPE
               MOVE WS-SUM-TYPE-DESC(WS-TYPE-IDX)
                   TO WS-DTL-DESC
               MOVE WS-SUM-COUNT(WS-TYPE-IDX)
                   TO WS-DTL-COUNT
               MOVE WS-SUM-DEBIT-TOT(WS-TYPE-IDX)
                   TO WS-DTL-DEBITS
               MOVE WS-SUM-CREDIT-TOT(WS-TYPE-IDX)
                   TO WS-DTL-CREDITS
               MOVE WS-RPT-DETAIL TO FS-SUM-LINE
               WRITE FS-SUM-LINE
               IF NOT SUMR-OK
                   DISPLAY "DLYTRXN: WRITE error on SUMRPT."
                           " Status: " WS-SUMR-STATUS
               END-IF
           END-PERFORM

      *    Grand total line
           MOVE ALL "-" TO FS-SUM-LINE
           WRITE FS-SUM-LINE
           MOVE WS-TOTAL-VALID TO WS-TOT-COUNT
           MOVE WS-GRAND-DEBITS TO WS-TOT-DEBITS
           MOVE WS-GRAND-CREDITS TO WS-TOT-CREDITS
           MOVE WS-RPT-TOTAL-LINE TO FS-SUM-LINE
           WRITE FS-SUM-LINE
           .

       4000-RECONCILE-TOTALS.
      *    -------------------------------------------------------
      *    Display control totals for reconciliation and set
      *    the return code based on processing results.
      *    -------------------------------------------------------
           DISPLAY " "
           DISPLAY "DLYTRXN: ===== CONTROL TOTALS ====="
           MOVE WS-TOTAL-RECORDS TO WS-DISP-COUNT
           DISPLAY "  Total records read:     " WS-DISP-COUNT
           MOVE WS-TOTAL-VALID TO WS-DISP-COUNT
           DISPLAY "  Valid records:          " WS-DISP-COUNT
           MOVE WS-TOTAL-ERRORS TO WS-DISP-COUNT
           DISPLAY "  Error records:          " WS-DISP-COUNT
           DISPLAY "  Grand debit total:      " WS-GRAND-DEBITS
           DISPLAY "  Grand credit total:     " WS-GRAND-CREDITS
           DISPLAY "DLYTRXN: =========================="

      *    Set return code
           IF ABORT-PROCESSING
               DISPLAY "DLYTRXN: Processing aborted. RC=12"
               MOVE 12 TO WS-RETURN-CODE
           ELSE IF WS-TOTAL-ERRORS > ZERO
               DISPLAY "DLYTRXN: Completed with errors. RC=4"
               MOVE 4 TO WS-RETURN-CODE
           ELSE
               DISPLAY "DLYTRXN: Completed successfully. RC=0"
               MOVE 0 TO WS-RETURN-CODE
           END-IF
           .

       9000-FINALIZE.
           CLOSE TRANSACTION-FILE
           IF NOT TRAN-OK AND NOT TRAN-EOF
               DISPLAY "DLYTRXN: CLOSE error on TRANFILE. "
                       "Status: " WS-TRAN-STATUS
           END-IF
           CLOSE SUMMARY-REPORT
           CLOSE ERROR-REPORT
           .

The Companion JCL

//DLYTRXNJ JOB (ACCT),'DAILY TRANS PROC',
//         CLASS=A,MSGCLASS=X,
//         MSGLEVEL=(1,1),
//         NOTIFY=&SYSUID
//*================================================================
//* JOB:     DLYTRXNJ
//* PURPOSE: EXECUTE THE DAILY TRANSACTION PROCESSING PROGRAM
//*          READS DAILY TRANSACTION FILE AND PRODUCES
//*          SUMMARY AND ERROR REPORTS
//* SCHEDULE: DAILY AT 22:00 CST
//*================================================================
//*
//*-------- STEP 1: EXECUTE THE TRANSACTION PROCESSOR -------------
//*
//STEP01   EXEC PGM=DLYTRXN
//STEPLIB  DD DSN=PIONEER.PROD.LOADLIB,DISP=SHR
//*
//*  INPUT: DAILY TRANSACTION FILE
//*
//TRANFILE DD DSN=PIONEER.DAILY.TRANS.D&LYYMMDD,
//            DISP=SHR,
//            DCB=(RECFM=FB,LRECL=80,BLKSIZE=27920)
//*
//*  OUTPUT: SUMMARY REPORT
//*
//SUMRPT   DD DSN=PIONEER.DAILY.SUMMARY.D&LYYMMDD,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(1,1),RLSE),
//            DCB=(RECFM=FB,LRECL=132,BLKSIZE=27984)
//*
//*  OUTPUT: ERROR REPORT
//*
//ERRRPT   DD DSN=PIONEER.DAILY.ERRORS.D&LYYMMDD,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(1,1),RLSE),
//            DCB=(RECFM=FB,LRECL=132,BLKSIZE=27984)
//*
//*  SYSOUT FOR DISPLAY MESSAGES
//*
//SYSOUT   DD SYSOUT=*
//*
//*-------- STEP 2: PRINT SUMMARY REPORT TO SPOOL -----------------
//*
//STEP02   EXEC PGM=IEBGENER,
//         COND=(8,LT,STEP01)
//SYSUT1   DD DSN=PIONEER.DAILY.SUMMARY.D&LYYMMDD,
//            DISP=SHR
//SYSUT2   DD SYSOUT=A,
//            DCB=(RECFM=FB,LRECL=132,BLKSIZE=27984)
//SYSPRINT DD SYSOUT=*
//SYSIN    DD DUMMY
//*
//*-------- STEP 3: PRINT ERROR REPORT IF ERRORS FOUND ------------
//*
//STEP03   EXEC PGM=IEBGENER,
//         COND=(0,EQ,STEP01)
//SYSUT1   DD DSN=PIONEER.DAILY.ERRORS.D&LYYMMDD,
//            DISP=SHR
//SYSUT2   DD SYSOUT=A,
//            DCB=(RECFM=FB,LRECL=132,BLKSIZE=27984)
//SYSPRINT DD SYSOUT=*
//SYSIN    DD DUMMY

Solution Walkthrough

File Status Checking: The Non-Negotiable Practice

Every file in the program has a dedicated FILE STATUS variable, and every I/O operation checks it immediately. The pattern is consistent throughout:

           OPEN INPUT TRANSACTION-FILE
           IF NOT TRAN-OK
               DISPLAY "DLYTRXN: FATAL - Cannot open "
                       "TRANFILE. Status: " WS-TRAN-STATUS
               MOVE 16 TO WS-RETURN-CODE
               STOP RUN
           END-IF

The file status "00" means success. "10" means end-of-file. Any value from "30" through "99" indicates an error. The 88-level conditions make these checks readable and self-documenting.

The Priming Read Pattern

The program uses the classic "priming read" pattern for sequential file processing:

  1. Read the first record before entering the processing loop
  2. Process the current record
  3. Read the next record at the end of the processing loop
  4. The loop terminates when EOF is detected

This pattern ensures that the EOF condition is tested before attempting to process a record, and that the record in the buffer is always a valid, unprocessed record when the loop body executes.

Error Threshold Abort

The program tracks the error rate and aborts if errors exceed 1% of records processed. This prevents a corrupted input file from producing a misleading summary report:

               IF WS-TOTAL-RECORDS > 100
                   COMPUTE WS-ERROR-RATE =
                       WS-TOTAL-ERRORS / WS-TOTAL-RECORDS
                   IF WS-ERROR-RATE > WS-ERROR-THRESHOLD
                       SET ABORT-PROCESSING TO TRUE
                   END-IF
               END-IF

The check is only performed after 100 records to avoid premature abort on a small number of early errors.

Return Code Communication via JCL

The COBOL program sets RETURN-CODE (a special register) to communicate results to JCL: - RC=0: Clean completion, all records valid - RC=4: Completed with some errors (within threshold) - RC=8: Error threshold exceeded - RC=12: Aborted due to excessive errors - RC=16: Fatal error (file open failure)

The JCL uses COND parameters to control subsequent steps based on this return code. Step 2 runs only if Step 1 returned less than 8 (success or minor errors). Step 3 (error report printing) runs only if Step 1 returned a non-zero code.


Lessons Learned

1. File Status Checking Is Not Optional

Every COBOL program that processes files in production must check FILE STATUS after every I/O operation. Without it, a file open failure causes unpredictable behavior rather than a clean abort. Marcus's rule: "If it touches a file, check the status."

2. The Priming Read Pattern Eliminates Off-by-One Errors

The priming read ensures that end-of-file is detected before processing, not during. Without it, the program might attempt to process the EOF indicator as if it were a data record.

3. Return Codes Bridge COBOL and JCL

The RETURN-CODE special register is the primary mechanism for a COBOL program to communicate its completion status to the JCL job stream. Well-defined return code conventions enable automated job scheduling and error handling.

4. Error Thresholds Prevent Garbage-In, Garbage-Out

A batch program that blindly processes a corrupted file and produces totals based on partial data is worse than one that aborts early and clearly. The threshold-based abort protects downstream systems from bad data.

5. Control Totals Enable Reconciliation

The grand debit and credit totals displayed at the end of the run enable the operations team to verify that the program processed the expected volume. If the input file was supposed to contain 250,000 records and the program reports 200,000, something went wrong upstream.


Discussion Questions

  1. The program uses BLOCK CONTAINS 0 RECORDS on the transaction file FD. What does this mean, and why is it preferable to specifying a fixed block size in the COBOL program?

  2. The validation logic uses a sequential check pattern where each check is only performed if the previous check passed. What are the trade-offs of this approach versus performing all checks and accumulating multiple error reasons per record?

  3. The JCL uses &LYYMMDD as a symbolic parameter in data set names. What is this, and how does it help manage daily processing? What happens if the job is rerun on the same day?

  4. The error report is written to a sequential file and then printed in a subsequent JCL step. Why not write directly to SYSOUT from the COBOL program? What advantage does the intermediate file provide?

  5. The return code 16 is used for fatal errors (file open failure). Why is this value chosen rather than, say, 99 or -1? What is the significance of return code values in the z/OS environment?

  6. The program accumulates debits and credits separately rather than computing a net total. Why is this important for reconciliation? What would a net-only approach miss?

  7. If the transaction file contained 500,000 records and the program needed to process them within a 30-minute batch window, what performance considerations would be important? How do BLOCK CONTAINS, BUFFERING, and I/O techniques affect throughput?