30 min read

You have arrived at the summit. Over the course of forty chapters, you have progressed from writing your first IDENTIFICATION DIVISION to mastering VSAM file processing, embedded SQL, CICS transactions, JCL job streams, object-oriented COBOL, and...

Chapter 41: Capstone Project -- Building a Complete Banking Application

Part IX - Capstone Projects and Career Path

You have arrived at the summit. Over the course of forty chapters, you have progressed from writing your first IDENTIFICATION DIVISION to mastering VSAM file processing, embedded SQL, CICS transactions, JCL job streams, object-oriented COBOL, and the testing practices that safeguard mission-critical systems. Now it is time to bring everything together.

In this chapter, you will build Community Bank, a complete banking application that exercises nearly every skill covered in this textbook. This is not a toy program or a simplified demo. It is a multi-program system with customer management, account management, transaction processing, batch settlement, interest calculation, statement generation, and regulatory reporting -- the same subsystems that power real core banking platforms processing trillions of dollars every day.

The application follows the architecture patterns used in production mainframe banking systems: a main menu program that dispatches to subsystem programs via the CALL statement, shared copybooks that enforce consistent data layouts across all programs, VSAM KSDS files for master data, sequential files for transaction input and report output, SORT for transaction ordering, and JCL to orchestrate the batch processing cycle.

You will not simply type in code and compile it. You will walk through the complete software development lifecycle: analyzing requirements, designing data structures, building a modular program architecture, writing and testing individual subsystems, and integrating everything into a working whole. Along the way, every section will point back to the specific chapters where each technique was first introduced, reinforcing the connections between the foundational skills and their real-world application.

Let us begin.


41.1 Project Overview and Requirements Analysis

The Business Context

Community Bank is a small regional bank with approximately 50,000 customers and 120,000 accounts. The bank offers checking accounts, savings accounts, money market accounts, and certificates of deposit. It processes an average of 75,000 transactions per day through branch tellers, ATMs, online banking, and ACH transfers.

The bank's technology team has been asked to build a new core banking system that handles the following business functions:

  1. Customer Management: Add new customers, update customer information, inquire on customer records, and deactivate customers who close all accounts.
  2. Account Management: Open new accounts, close accounts, modify account attributes, and perform account inquiries that display balances and recent activity.
  3. Transaction Processing: Process deposits, withdrawals, and transfers in real time. Assess fees when business rules are triggered (overdraft, monthly maintenance, wire transfer). Validate every transaction against business rules before posting.
  4. Batch Processing: Run end-of-day settlement to post pending transactions, calculate daily interest accrual for all interest-bearing accounts, and generate customer statements on each account's statement cycle day.
  5. Report Generation: Produce a daily transaction summary report, monthly account statements, and a Currency Transaction Report (CTR) for transactions exceeding $10,000 (a regulatory requirement under the Bank Secrecy Act).

Functional Requirements

Each subsystem has specific functional requirements that map directly to COBOL techniques you have already learned:

Requirement COBOL Technique Chapter Reference
Store and retrieve customer records by key VSAM KSDS, indexed file I/O Chapters 12-13
Validate input data (numeric fields, dates, ranges) IF/EVALUATE, class tests, 88-levels Chapter 7
Compute interest using compound formulas COMPUTE, decimal arithmetic Chapter 6, Chapter 33
Sort transactions by account and date SORT verb with INPUT/OUTPUT PROCEDURE Chapter 14
Generate formatted printed reports Report WRITE with headers, footers, page control Chapter 15
Share data layouts across programs COPY statement and copybooks Chapter 18
Call subsystem programs from a menu CALL statement with USING and RETURNING Chapter 17
Handle file I/O errors gracefully Declaratives, file status checking Chapter 16
Process sequential transaction files Sequential file READ/WRITE Chapter 11
Run batch jobs with step dependencies JCL with COND parameters Chapter 27
Perform string manipulation for formatting STRING, UNSTRING, INSPECT, reference modification Chapter 9
Use tables for lookup operations OCCURS, SEARCH, subscripting Chapter 10

Non-Functional Requirements

Beyond features, the application must meet quality standards that reflect real production expectations:

  • Data integrity: Every transaction must be fully posted or fully rejected. No partial updates.
  • Audit trail: Every change to customer or account records must be traceable through the transaction history file.
  • Error handling: Every file operation must check the file status code. Every CALL must handle the ON EXCEPTION condition. Every numeric input must be validated before use.
  • Performance: Batch programs must process the full transaction file in a single pass. No unnecessary re-reads or redundant I/O.
  • Maintainability: All data layouts live in copybooks. All magic numbers live in named constants. All paragraph names are descriptive and follow a consistent naming convention.

41.2 System Architecture

The Big Picture

The Community Bank application consists of eight COBOL programs, four shared copybooks, three VSAM files, and multiple sequential files. The following diagram illustrates the system structure:

                     +-------------------+
                     |    CAPMAIN        |
                     |  Main Menu and    |
                     |  Dispatcher       |
                     +--------+----------+
                              |
          +-------+-------+---+---+-------+-------+
          |       |       |       |       |       |
     +----+--+ +--+---+ +-+----+ +--+--+ +--+--+ +--+---+
     |CAPAMGMT| |CAPTRANS| |CAPINQRY| |CAPINTRN| |CAPSTMNT| |CAPRPORT|
     |Account | |Trans.  | |Inquiry | |Interest| |Statmt. | |Reports |
     |Mgmt    | |Process | |        | |Calc    | |Gen     | |        |
     +----+---+ +---+----+ +---+----+ +---+----+ +---+----+ +---+----+
          |         |           |          |          |          |
          +----+----+-----+----+----+-----+----+-----+----+-----+
               |          |         |          |          |
          +----+----+ +---+----+ +--+-----+   |    +-----+-----+
          |CAPVALID | |CAPERROR| |Copybooks|   |    |JCL Procs  |
          |Validate | |Error   | |(.CPY)   |   |    |           |
          |Routine  | |Handler | +---------+   |    +-----------+
          +---------+ +--------+               |
                                          +----+----+
                                          |VSAM     |
                                          |Files    |
                                          +---------+

Program Inventory

Program ID Purpose Type
CAPMAIN Main menu, dispatcher, session control Online/Interactive
CAPAMGMT Account open, close, modify Called subprogram
CAPTRANS Deposit, withdrawal, transfer, fee Called subprogram
CAPINQRY Customer and account inquiry display Called subprogram
CAPINTRN Daily interest accrual calculation Batch
CAPSTMNT Monthly statement generation Batch
CAPRPORT Daily summary and regulatory reports Batch
CAPVALID Shared validation routines Called subprogram
CAPERROR Centralized error handler Called subprogram

File Architecture

The system uses three VSAM Key-Sequenced Data Sets for persistent storage and sequential files for batch input, output, and reports:

VSAM KSDS Files:

  • CUSTFILE -- Customer master file. Primary key: CUST-NUMBER (10 bytes). Record length: 500 bytes. Layout defined in capstone-customer.cpy.
  • ACCTFILE -- Account master file. Primary key: ACCT-NUMBER (10 bytes). Alternate key: ACCT-CUST-NUMBER (non-unique, with duplicates). Record length: 400 bytes. Layout defined in capstone-account.cpy.
  • TXNHFILE -- Transaction history file. Primary key: TXN-ID (12 bytes). Record length: 300 bytes. Layout defined in capstone-transaction.cpy.

Sequential Files:

  • TXNINPUT -- Daily transaction input file (input to batch processing).
  • STMTFILE -- Statement output file (produced by CAPSTMNT).
  • RPTFILE -- Report output file (produced by CAPRPORT).
  • ERRFILE -- Error/reject file for transactions that fail validation.

Copybook Architecture

The four copybooks in the code/ directory form the backbone of the data architecture. Every program that touches customer data includes capstone-customer.cpy. Every program that touches account data includes capstone-account.cpy. This ensures that all programs agree on field names, sizes, and positions -- a practice you first learned in Chapter 18 (Copybooks and the COPY Statement).

The capstone-constants.cpy copybook is included by every program in the system. It defines return codes, account type codes, transaction type codes, interest rates, business limits, fee schedules, the common communication area used for inter-program calls, and shared work fields. By placing constants in a copybook rather than hard-coding them in each program, a change to a fee amount or a business limit requires modifying a single file and recompiling the affected programs.

The capstone-transaction.cpy copybook defines both the transaction input record and the transaction display format used for inquiries and statements. It also includes the transaction summary totals group used by reporting programs.


41.3 The Data Design

Customer Record Design Rationale

Open the file capstone-customer.cpy and examine the CUSTOMER-RECORD layout. Several design decisions merit discussion.

The customer number (CUST-NUMBER, PIC X(10)) is alphanumeric rather than purely numeric. This is deliberate -- many banking systems use customer numbers that include a branch prefix or a check digit suffix. Making the field alphanumeric gives the system flexibility to accommodate different numbering schemes without data conversion. This design pattern was discussed in Chapter 3 (Data Types and the PICTURE Clause) where you learned the difference between PIC X and PIC 9 fields.

The name group (CUST-NAME) uses three subfields: last name, first name, and middle initial. This structure supports the formatted display of names in "Last, First M." format, which you build using the STRING verb (Chapter 9). Keeping the name components separate rather than storing a single free-form name field makes sorting, searching, and regulatory reporting far easier.

The SSN field (CUST-SSN, PIC X(09)) is stored without dashes. Formatting dashes for display is trivial with reference modification or STRING, but storing them in the record would waste two bytes per record multiplied by 50,000 customers, and would complicate validation logic. This principle of storing raw data and formatting for display is a recurring theme in COBOL data design.

The 88-level condition names on CUST-TYPE and CUST-STATUS (Chapter 7) make code self-documenting. Instead of writing IF CUST-STATUS = 'A', the programmer writes IF CUST-STAT-ACTIVE, which is immediately readable without consulting a code table.

Account Record Design Rationale

The account record in capstone-account.cpy is 400 bytes and contains more numeric fields than the customer record, reflecting the account's role as the financial center of the system.

Notice the multiple balance fields: ACCT-CURRENT-BALANCE, ACCT-AVAILABLE-BALANCE, ACCT-PENDING-DEBITS, ACCT-PENDING-CREDITS, and ACCT-HOLD-AMOUNT. In real banking systems, the "current balance" (also called the ledger balance) and the "available balance" are different numbers. The available balance accounts for pending transactions, holds, and overdraft limits. This distinction is critical for transaction validation -- a customer can only withdraw up to their available balance, not their current balance.

All monetary fields use COMP-3 (packed decimal) storage, as discussed in Chapter 3. Packed decimal is the standard for financial data on mainframes because it preserves exact decimal precision and supports efficient arithmetic operations. The signed fields (PIC S9(11)V99 COMP-3) occupy only 7 bytes of storage while representing values up to plus or minus 99,999,999,999.99 -- more than adequate for any individual account balance.

The account flags group (ACCT-FLAGS) demonstrates the efficient use of single-byte flag fields with 88-level conditions. Each flag consumes only one byte but encodes a business rule: whether ATM access is blocked, whether the customer has opted out of overdraft protection, whether the account is subject to escheatment, garnishment, or 1099 reporting exemption, and the customer's statement delivery preference.

Transaction Record Design

The transaction record in capstone-transaction.cpy at 300 bytes captures everything needed to reconstruct the complete history of any account. The TXN-BALANCE-BEFORE and TXN-BALANCE-AFTER fields create a self-verifying chain: for any sequence of transactions on an account, each transaction's "before" balance must equal the previous transaction's "after" balance. This is an auditor's most basic integrity check.

The TXN-CHANNEL field with its 88-level conditions records how the transaction was initiated (branch, ATM, online, mobile, ACH, wire, or batch), which is essential for both customer service and regulatory reporting.


41.4 Building the Main Menu Program (CAPMAIN)

The main menu program is the entry point for the interactive portion of the system. It displays a menu of options, accepts the user's choice, sets up the communication area, and dispatches to the appropriate subprogram via the CALL statement (Chapter 17).

       IDENTIFICATION DIVISION.
       PROGRAM-ID. CAPMAIN.
      *================================================================*
      * CAPMAIN - Community Bank Main Menu and Dispatcher              *
      * This program serves as the central hub for all interactive     *
      * banking operations. It presents a menu to the user, accepts    *
      * a selection, populates the communication area, and calls       *
      * the appropriate subprogram.                                    *
      *================================================================*

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           FUNCTION ALL INTRINSIC.

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       COPY 'capstone-constants.cpy'.
       COPY 'capstone-customer.cpy'.
       COPY 'capstone-account.cpy'.

       01  WS-MENU-CHOICE          PIC 9(02)    VALUE ZERO.
       01  WS-CONTINUE-FLAG        PIC X(01)    VALUE 'Y'.
           88  WS-CONTINUE         VALUE 'Y' 'y'.
           88  WS-EXIT             VALUE 'N' 'n'.
       01  WS-INPUT-BUFFER         PIC X(80)    VALUE SPACES.
       01  WS-DISPLAY-LINE         PIC X(80)    VALUE SPACES.
       01  WS-CURRENT-USERID       PIC X(08)    VALUE SPACES.
       01  WS-SESSION-ACTIVE       PIC X(01)    VALUE 'Y'.
           88  SESSION-ACTIVE      VALUE 'Y'.
           88  SESSION-ENDED       VALUE 'N'.
       01  WS-SUB-PROGRAM          PIC X(08)    VALUE SPACES.

       PROCEDURE DIVISION.

       0000-MAIN-CONTROL.
           PERFORM 1000-INITIALIZE
           PERFORM 2000-PROCESS-MENU
               UNTIL SESSION-ENDED
           PERFORM 9000-TERMINATE
           STOP RUN
           .

       1000-INITIALIZE.
           MOVE FUNCTION CURRENT-DATE(1:8)
               TO WS-CURRENT-DATE
           MOVE FUNCTION CURRENT-DATE(9:8)
               TO WS-CURRENT-TIME
           MOVE 'TELLER01' TO WS-CURRENT-USERID
           INITIALIZE WS-COMM-AREA
           DISPLAY '========================================'
           DISPLAY '  COMMUNITY BANK MANAGEMENT SYSTEM'
           DISPLAY '  Date: ' WS-CURR-YEAR '/'
                               WS-CURR-MONTH '/'
                               WS-CURR-DAY
           DISPLAY '  User: ' WS-CURRENT-USERID
           DISPLAY '========================================'
           .

       2000-PROCESS-MENU.
           PERFORM 2100-DISPLAY-MENU
           PERFORM 2200-GET-SELECTION
           EVALUATE WS-MENU-CHOICE
               WHEN 1
                   PERFORM 3000-CUSTOMER-MANAGEMENT
               WHEN 2
                   PERFORM 4000-ACCOUNT-MANAGEMENT
               WHEN 3
                   PERFORM 5000-TRANSACTION-PROCESSING
               WHEN 4
                   PERFORM 6000-ACCOUNT-INQUIRY
               WHEN 5
                   PERFORM 7000-CUSTOMER-INQUIRY
               WHEN 99
                   SET SESSION-ENDED TO TRUE
               WHEN OTHER
                   DISPLAY 'Invalid selection. Please try again.'
           END-EVALUATE
           .

       2100-DISPLAY-MENU.
           DISPLAY SPACES
           DISPLAY '========== MAIN MENU ==================='
           DISPLAY '  1. Customer Management'
           DISPLAY '  2. Account Management'
           DISPLAY '  3. Transaction Processing'
           DISPLAY '  4. Account Inquiry'
           DISPLAY '  5. Customer Inquiry'
           DISPLAY ' 99. Exit System'
           DISPLAY '========================================'
           DISPLAY 'Enter selection: ' WITH NO ADVANCING
           .

       2200-GET-SELECTION.
           ACCEPT WS-MENU-CHOICE FROM CONSOLE
           .

       3000-CUSTOMER-MANAGEMENT.
           DISPLAY SPACES
           DISPLAY '--- Customer Management ---'
           DISPLAY '  1. Add New Customer'
           DISPLAY '  2. Update Customer'
           DISPLAY '  3. Deactivate Customer'
           DISPLAY 'Enter choice: ' WITH NO ADVANCING
           ACCEPT WS-MENU-CHOICE FROM CONSOLE
           EVALUATE WS-MENU-CHOICE
               WHEN 1
                   SET COMM-FUNC-OPEN TO TRUE
               WHEN 2
                   SET COMM-FUNC-MODIFY TO TRUE
               WHEN 3
                   SET COMM-FUNC-CLOSE TO TRUE
               WHEN OTHER
                   DISPLAY 'Invalid choice.'
                   GO TO 3000-EXIT
           END-EVALUATE
           DISPLAY 'Enter Customer Number: '
               WITH NO ADVANCING
           ACCEPT COMM-CUST-NUMBER FROM CONSOLE
           MOVE WS-CURRENT-USERID TO COMM-USER-ID
           CALL PGM-ACCOUNT-MGMT
               USING WS-COMM-AREA CUSTOMER-RECORD
               ON EXCEPTION
                   DISPLAY 'ERROR: Unable to load '
                       PGM-ACCOUNT-MGMT
                   MOVE RC-FATAL TO COMM-RETURN-CODE
               NOT ON EXCEPTION
                   PERFORM 8000-DISPLAY-RESULT
           END-CALL
           .
       3000-EXIT.
           EXIT.

       4000-ACCOUNT-MANAGEMENT.
           DISPLAY SPACES
           DISPLAY '--- Account Management ---'
           DISPLAY '  1. Open New Account'
           DISPLAY '  2. Close Account'
           DISPLAY '  3. Modify Account'
           DISPLAY 'Enter choice: ' WITH NO ADVANCING
           ACCEPT WS-MENU-CHOICE FROM CONSOLE
           EVALUATE WS-MENU-CHOICE
               WHEN 1
                   SET COMM-FUNC-OPEN TO TRUE
               WHEN 2
                   SET COMM-FUNC-CLOSE TO TRUE
               WHEN 3
                   SET COMM-FUNC-MODIFY TO TRUE
               WHEN OTHER
                   DISPLAY 'Invalid choice.'
                   GO TO 4000-EXIT
           END-EVALUATE
           DISPLAY 'Enter Account Number: '
               WITH NO ADVANCING
           ACCEPT COMM-ACCT-NUMBER FROM CONSOLE
           DISPLAY 'Enter Customer Number: '
               WITH NO ADVANCING
           ACCEPT COMM-CUST-NUMBER FROM CONSOLE
           MOVE WS-CURRENT-USERID TO COMM-USER-ID
           CALL PGM-ACCOUNT-MGMT
               USING WS-COMM-AREA ACCOUNT-RECORD
               ON EXCEPTION
                   DISPLAY 'ERROR: Unable to load '
                       PGM-ACCOUNT-MGMT
                   MOVE RC-FATAL TO COMM-RETURN-CODE
               NOT ON EXCEPTION
                   PERFORM 8000-DISPLAY-RESULT
           END-CALL
           .
       4000-EXIT.
           EXIT.

       5000-TRANSACTION-PROCESSING.
           DISPLAY SPACES
           DISPLAY '--- Transaction Processing ---'
           DISPLAY '  1. Deposit'
           DISPLAY '  2. Withdrawal'
           DISPLAY '  3. Transfer'
           DISPLAY 'Enter choice: ' WITH NO ADVANCING
           ACCEPT WS-MENU-CHOICE FROM CONSOLE
           EVALUATE WS-MENU-CHOICE
               WHEN 1
                   SET COMM-FUNC-DEPOSIT TO TRUE
               WHEN 2
                   SET COMM-FUNC-WITHDRAW TO TRUE
               WHEN 3
                   SET COMM-FUNC-TRANSFER TO TRUE
               WHEN OTHER
                   DISPLAY 'Invalid choice.'
                   GO TO 5000-EXIT
           END-EVALUATE
           DISPLAY 'Enter Account Number: '
               WITH NO ADVANCING
           ACCEPT COMM-ACCT-NUMBER FROM CONSOLE
           DISPLAY 'Enter Amount: ' WITH NO ADVANCING
           ACCEPT COMM-AMOUNT FROM CONSOLE
           IF COMM-FUNC-TRANSFER
               DISPLAY 'Enter Target Account: '
                   WITH NO ADVANCING
               ACCEPT COMM-TARGET-ACCT FROM CONSOLE
           END-IF
           MOVE WS-CURRENT-USERID TO COMM-USER-ID
           CALL PGM-TRANSACTION
               USING WS-COMM-AREA
                     ACCOUNT-RECORD
               ON EXCEPTION
                   DISPLAY 'ERROR: Unable to load '
                       PGM-TRANSACTION
                   MOVE RC-FATAL TO COMM-RETURN-CODE
               NOT ON EXCEPTION
                   PERFORM 8000-DISPLAY-RESULT
           END-CALL
           .
       5000-EXIT.
           EXIT.

       6000-ACCOUNT-INQUIRY.
           SET COMM-FUNC-INQUIRY TO TRUE
           DISPLAY 'Enter Account Number: '
               WITH NO ADVANCING
           ACCEPT COMM-ACCT-NUMBER FROM CONSOLE
           CALL PGM-INQUIRY
               USING WS-COMM-AREA
                     ACCOUNT-RECORD
               ON EXCEPTION
                   DISPLAY 'ERROR: Unable to load '
                       PGM-INQUIRY
               NOT ON EXCEPTION
                   PERFORM 8000-DISPLAY-RESULT
           END-CALL
           .

       7000-CUSTOMER-INQUIRY.
           SET COMM-FUNC-INQUIRY TO TRUE
           DISPLAY 'Enter Customer Number: '
               WITH NO ADVANCING
           ACCEPT COMM-CUST-NUMBER FROM CONSOLE
           CALL PGM-INQUIRY
               USING WS-COMM-AREA
                     CUSTOMER-RECORD
               ON EXCEPTION
                   DISPLAY 'ERROR: Unable to load '
                       PGM-INQUIRY
               NOT ON EXCEPTION
                   PERFORM 8000-DISPLAY-RESULT
           END-CALL
           .

       8000-DISPLAY-RESULT.
           EVALUATE COMM-RETURN-CODE
               WHEN RC-SUCCESS
                   DISPLAY 'Operation completed successfully.'
               WHEN RC-WARNING
                   DISPLAY 'Warning: ' COMM-ERROR-MSG
               WHEN RC-ERROR
                   DISPLAY 'Error: ' COMM-ERROR-MSG
               WHEN OTHER
                   DISPLAY 'Severe error: ' COMM-ERROR-MSG
           END-EVALUATE
           INITIALIZE WS-COMM-AREA
           .

       9000-TERMINATE.
           DISPLAY SPACES
           DISPLAY 'Community Bank system session ended.'
           DISPLAY 'Goodbye.'
           .

This program demonstrates several key patterns. The EVALUATE statement (Chapter 7) provides clean multi-way branching for menu dispatch. The CALL statement (Chapter 17) uses the program name constants from the copybook rather than hard-coded literals, allowing program names to be changed in one place. The ON EXCEPTION phrase handles the case where a called program cannot be loaded -- essential for robust production systems. The communication area (WS-COMM-AREA from capstone-constants.cpy) serves as the inter-program parameter block, passing the function code, account number, amount, and other context between the menu and the subsystem programs.


41.5 Building the Transaction Processing Subsystem (CAPTRANS)

The transaction processing program is the financial heart of the system. It receives a communication area from the caller, validates the requested transaction against business rules, updates the account balance, writes a transaction history record, and returns a status code. This program exercises VSAM random access (Chapter 12), decimal arithmetic (Chapter 6), validation logic (Chapter 7), and subprogram conventions (Chapter 17).

       IDENTIFICATION DIVISION.
       PROGRAM-ID. CAPTRANS.
      *================================================================*
      * CAPTRANS - Transaction Processing Subsystem                    *
      * Handles deposits, withdrawals, transfers, and fee assessment.  *
      * Called from CAPMAIN via CALL using WS-COMM-AREA.               *
      *================================================================*

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT ACCT-FILE
               ASSIGN TO ACCTFILE
               ORGANIZATION IS INDEXED
               ACCESS MODE IS RANDOM
               RECORD KEY IS ACCT-NUMBER
               FILE STATUS IS WS-ACCT-STATUS.
           SELECT TXN-HIST-FILE
               ASSIGN TO TXNHFILE
               ORGANIZATION IS INDEXED
               ACCESS MODE IS RANDOM
               RECORD KEY IS TXN-ID
               FILE STATUS IS WS-TXN-STATUS.

       DATA DIVISION.
       FILE SECTION.
       FD  ACCT-FILE.
       COPY 'capstone-account.cpy'
           REPLACING ==01  ACCOUNT-RECORD==
               BY    ==01  ACCT-FILE-RECORD==.

       FD  TXN-HIST-FILE.
       COPY 'capstone-transaction.cpy'
           REPLACING ==01  TRANSACTION-RECORD==
               BY    ==01  TXN-HIST-RECORD==.

       WORKING-STORAGE SECTION.

       COPY 'capstone-constants.cpy'.
       COPY 'capstone-account.cpy'.
       COPY 'capstone-transaction.cpy'.

       01  WS-ACCT-WORK            PIC S9(11)V99 COMP-3.
       01  WS-TXN-SEQ              PIC 9(08)    VALUE ZERO.
       01  WS-TXN-ID-WORK          PIC X(12)    VALUE SPACES.
       01  WS-TARGET-BALANCE       PIC S9(11)V99 COMP-3.
       01  WS-FILES-OPEN           PIC X(01)    VALUE 'N'.
           88  FILES-ARE-OPEN      VALUE 'Y'.

       LINKAGE SECTION.
       01  LS-COMM-AREA.
           05  LS-FUNCTION         PIC X(04).
           05  LS-RETURN-CODE      PIC S9(4) COMP.
           05  LS-REASON-CODE      PIC S9(4) COMP.
           05  LS-ERROR-MSG        PIC X(80).
           05  LS-ACCT-NUMBER      PIC X(10).
           05  LS-CUST-NUMBER      PIC X(10).
           05  LS-AMOUNT           PIC S9(11)V99 COMP-3.
           05  LS-TARGET-ACCT      PIC X(10).
           05  LS-PROCESS-DATE     PIC X(10).
           05  LS-USER-ID          PIC X(08).
           05  LS-TIMESTAMP        PIC X(26).

       01  LS-ACCOUNT-REC.
           05  LS-ACCT-DATA        PIC X(400).

       PROCEDURE DIVISION USING LS-COMM-AREA LS-ACCOUNT-REC.

       0000-MAIN-CONTROL.
           PERFORM 1000-INITIALIZE
           IF LS-RETURN-CODE = RC-SUCCESS
               PERFORM 2000-PROCESS-TRANSACTION
           END-IF
           PERFORM 9000-TERMINATE
           GOBACK
           .

       1000-INITIALIZE.
           MOVE RC-SUCCESS TO LS-RETURN-CODE
           MOVE SPACES TO LS-ERROR-MSG
           OPEN I-O ACCT-FILE
           IF WS-ACCT-STATUS NOT = '00'
               MOVE RC-FATAL TO LS-RETURN-CODE
               STRING 'Cannot open account file. Status: '
                   WS-ACCT-STATUS
                   DELIMITED BY SIZE
                   INTO LS-ERROR-MSG
               GO TO 1000-EXIT
           END-IF
           OPEN I-O TXN-HIST-FILE
           IF WS-TXN-STATUS NOT = '00'
               MOVE RC-FATAL TO LS-RETURN-CODE
               STRING 'Cannot open txn history. Status: '
                   WS-TXN-STATUS
                   DELIMITED BY SIZE
                   INTO LS-ERROR-MSG
               CLOSE ACCT-FILE
               GO TO 1000-EXIT
           END-IF
           SET FILES-ARE-OPEN TO TRUE
           .
       1000-EXIT.
           EXIT.

       2000-PROCESS-TRANSACTION.
           MOVE LS-ACCT-NUMBER TO ACCT-NUMBER
           READ ACCT-FILE INTO ACCOUNT-RECORD
               KEY IS ACCT-NUMBER
           IF WS-ACCT-STATUS NOT = '00'
               MOVE RC-ERROR TO LS-RETURN-CODE
               MOVE 'Account not found.' TO LS-ERROR-MSG
               GO TO 2000-EXIT
           END-IF
           IF NOT ACCT-IS-ACTIVE
               MOVE RC-ERROR TO LS-RETURN-CODE
               MOVE 'Account is not active.' TO LS-ERROR-MSG
               GO TO 2000-EXIT
           END-IF
           EVALUATE LS-FUNCTION
               WHEN 'DEPT'
                   PERFORM 3000-PROCESS-DEPOSIT
               WHEN 'WITH'
                   PERFORM 4000-PROCESS-WITHDRAWAL
               WHEN 'XFER'
                   PERFORM 5000-PROCESS-TRANSFER
               WHEN OTHER
                   MOVE RC-ERROR TO LS-RETURN-CODE
                   MOVE 'Invalid function code.'
                       TO LS-ERROR-MSG
           END-EVALUATE
           .
       2000-EXIT.
           EXIT.

       3000-PROCESS-DEPOSIT.
           IF LS-AMOUNT <= ZERO
               MOVE RC-ERROR TO LS-RETURN-CODE
               MOVE 'Deposit amount must be positive.'
                   TO LS-ERROR-MSG
               GO TO 3000-EXIT
           END-IF
           MOVE ACCT-CURRENT-BALANCE TO TXN-BALANCE-BEFORE
           ADD LS-AMOUNT TO ACCT-CURRENT-BALANCE
           ADD LS-AMOUNT TO ACCT-AVAILABLE-BALANCE
           ADD LS-AMOUNT TO ACCT-MTD-DEPOSITS
           ADD LS-AMOUNT TO ACCT-YTD-DEPOSITS
           ADD 1 TO ACCT-MTD-TXN-COUNT
           MOVE ACCT-CURRENT-BALANCE TO TXN-BALANCE-AFTER
           MOVE FUNCTION CURRENT-DATE(1:8)
               TO ACCT-LAST-TXN-DATE
           PERFORM 7000-WRITE-ACCOUNT
           PERFORM 8000-WRITE-TXN-HISTORY
           .
       3000-EXIT.
           EXIT.

       4000-PROCESS-WITHDRAWAL.
           IF LS-AMOUNT <= ZERO
               MOVE RC-ERROR TO LS-RETURN-CODE
               MOVE 'Withdrawal amount must be positive.'
                   TO LS-ERROR-MSG
               GO TO 4000-EXIT
           END-IF
           IF LS-AMOUNT > LIMIT-MAX-WITHDRAWAL
               MOVE RC-ERROR TO LS-RETURN-CODE
               MOVE 'Exceeds maximum withdrawal limit.'
                   TO LS-ERROR-MSG
               GO TO 4000-EXIT
           END-IF
           IF LS-AMOUNT > ACCT-AVAILABLE-BALANCE
               IF ACCT-OD-OPTOUT
                   MOVE RC-ERROR TO LS-RETURN-CODE
                   MOVE 'Insufficient funds (OD opted out).'
                       TO LS-ERROR-MSG
                   GO TO 4000-EXIT
               ELSE
                   PERFORM 6000-ASSESS-OVERDRAFT-FEE
               END-IF
           END-IF
           MOVE ACCT-CURRENT-BALANCE TO TXN-BALANCE-BEFORE
           SUBTRACT LS-AMOUNT FROM ACCT-CURRENT-BALANCE
           SUBTRACT LS-AMOUNT FROM ACCT-AVAILABLE-BALANCE
           ADD LS-AMOUNT TO ACCT-MTD-WITHDRAWALS
           ADD LS-AMOUNT TO ACCT-YTD-WITHDRAWALS
           ADD 1 TO ACCT-MTD-TXN-COUNT
           MOVE ACCT-CURRENT-BALANCE TO TXN-BALANCE-AFTER
           MOVE FUNCTION CURRENT-DATE(1:8)
               TO ACCT-LAST-TXN-DATE
           PERFORM 7000-WRITE-ACCOUNT
           PERFORM 8000-WRITE-TXN-HISTORY
           .
       4000-EXIT.
           EXIT.

       5000-PROCESS-TRANSFER.
           IF LS-AMOUNT <= ZERO
               MOVE RC-ERROR TO LS-RETURN-CODE
               MOVE 'Transfer amount must be positive.'
                   TO LS-ERROR-MSG
               GO TO 5000-EXIT
           END-IF
           IF LS-AMOUNT > LIMIT-MAX-TRANSFER
               MOVE RC-ERROR TO LS-RETURN-CODE
               MOVE 'Exceeds maximum transfer limit.'
                   TO LS-ERROR-MSG
               GO TO 5000-EXIT
           END-IF
           IF LS-AMOUNT > ACCT-AVAILABLE-BALANCE
               MOVE RC-ERROR TO LS-RETURN-CODE
               MOVE 'Insufficient funds for transfer.'
                   TO LS-ERROR-MSG
               GO TO 5000-EXIT
           END-IF
      *    Debit the source account
           MOVE ACCT-CURRENT-BALANCE TO TXN-BALANCE-BEFORE
           SUBTRACT LS-AMOUNT FROM ACCT-CURRENT-BALANCE
           SUBTRACT LS-AMOUNT FROM ACCT-AVAILABLE-BALANCE
           ADD LS-AMOUNT TO ACCT-MTD-WITHDRAWALS
           ADD 1 TO ACCT-MTD-TXN-COUNT
           MOVE ACCT-CURRENT-BALANCE TO TXN-BALANCE-AFTER
           PERFORM 7000-WRITE-ACCOUNT
           PERFORM 8000-WRITE-TXN-HISTORY
      *    Credit the target account
           MOVE LS-TARGET-ACCT TO ACCT-NUMBER
           READ ACCT-FILE INTO ACCOUNT-RECORD
               KEY IS ACCT-NUMBER
           IF WS-ACCT-STATUS NOT = '00'
               MOVE RC-ERROR TO LS-RETURN-CODE
               MOVE 'Target account not found.'
                   TO LS-ERROR-MSG
               GO TO 5000-EXIT
           END-IF
           ADD LS-AMOUNT TO ACCT-CURRENT-BALANCE
           ADD LS-AMOUNT TO ACCT-AVAILABLE-BALANCE
           ADD LS-AMOUNT TO ACCT-MTD-DEPOSITS
           ADD 1 TO ACCT-MTD-TXN-COUNT
           PERFORM 7000-WRITE-ACCOUNT
           .
       5000-EXIT.
           EXIT.

       6000-ASSESS-OVERDRAFT-FEE.
           ADD FEE-OVERDRAFT TO ACCT-YTD-FEES
           SUBTRACT FEE-OVERDRAFT FROM ACCT-AVAILABLE-BALANCE
           .

       7000-WRITE-ACCOUNT.
           REWRITE ACCT-FILE-RECORD FROM ACCOUNT-RECORD
           IF WS-ACCT-STATUS NOT = '00'
               MOVE RC-SEVERE TO LS-RETURN-CODE
               STRING 'Account REWRITE failed. Status: '
                   WS-ACCT-STATUS
                   DELIMITED BY SIZE
                   INTO LS-ERROR-MSG
           END-IF
           .

       8000-WRITE-TXN-HISTORY.
           ADD 1 TO WS-TXN-SEQ
           STRING LS-ACCT-NUMBER WS-TXN-SEQ
               DELIMITED BY SIZE
               INTO WS-TXN-ID-WORK
           MOVE WS-TXN-ID-WORK TO TXN-ID
           MOVE LS-ACCT-NUMBER  TO TXN-ACCT-NUMBER
           MOVE LS-FUNCTION(1:2) TO TXN-TYPE
           MOVE LS-AMOUNT       TO TXN-AMOUNT
           MOVE FUNCTION CURRENT-DATE(1:8) TO TXN-DATE
           MOVE FUNCTION CURRENT-DATE(9:6) TO TXN-TIME
           SET TXN-POSTED       TO TRUE
           MOVE LS-USER-ID      TO TXN-USER-ID
           SET TXN-CHANNEL-BRANCH TO TRUE
           WRITE TXN-HIST-RECORD FROM TRANSACTION-RECORD
           IF WS-TXN-STATUS NOT = '00'
               MOVE RC-WARNING TO LS-RETURN-CODE
               STRING 'Txn history write failed. Status: '
                   WS-TXN-STATUS
                   DELIMITED BY SIZE
                   INTO LS-ERROR-MSG
           END-IF
           .

       9000-TERMINATE.
           IF FILES-ARE-OPEN
               CLOSE ACCT-FILE
               CLOSE TXN-HIST-FILE
           END-IF
           .

Key Design Decisions in CAPTRANS

File status checking on every I/O operation. Notice that every READ, WRITE, REWRITE, OPEN, and CLOSE operation is followed by a check of the file status variable. This practice, introduced in Chapter 16 (Declaratives and File Exception Handling), is not optional in production banking code. A file I/O failure that goes undetected can corrupt data silently, creating reconciliation nightmares that take days to resolve.

Balance-before and balance-after recording. Before modifying the account balance, the program captures the current balance in TXN-BALANCE-BEFORE. After the modification, it captures TXN-BALANCE-AFTER. This creates an auditable chain that regulators require and that internal audit teams verify during every examination.

Overdraft processing. The withdrawal logic checks whether the amount exceeds the available balance. If it does, and the customer has not opted out of overdraft coverage, the program assesses the overdraft fee by calling the 6000-ASSESS-OVERDRAFT-FEE paragraph. If the customer has opted out (ACCT-OD-OPTOUT evaluates to TRUE), the transaction is rejected. This is a direct implementation of the regulatory requirement under Regulation E, which requires banks to honor customer opt-out preferences.

Transfer as a two-phase operation. The transfer logic debits the source account first, then credits the target account. In a production system, this would be wrapped in a unit of work (using CICS syncpoint or DB2 commit) to ensure atomicity. Here, the sequential REWRITE operations illustrate the concept while keeping the code focused on COBOL mechanics.


41.6 Building the Account Inquiry Program (CAPINQRY)

The inquiry program reads customer and account records from VSAM and displays them in a formatted layout. It demonstrates VSAM random and sequential access, formatted output with edited PICTURE clauses, and the use of 88-level conditions to translate status codes into human-readable descriptions.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. CAPINQRY.
      *================================================================*
      * CAPINQRY - Customer and Account Inquiry Subsystem              *
      * Displays customer details, account details, or account         *
      * listing for a customer. Uses VSAM random and sequential        *
      * access patterns.                                               *
      *================================================================*

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT CUST-FILE
               ASSIGN TO CUSTFILE
               ORGANIZATION IS INDEXED
               ACCESS MODE IS RANDOM
               RECORD KEY IS CUST-NUMBER
               FILE STATUS IS WS-CUST-STATUS.
           SELECT ACCT-FILE
               ASSIGN TO ACCTFILE
               ORGANIZATION IS INDEXED
               ACCESS MODE IS DYNAMIC
               RECORD KEY IS ACCT-NUMBER
               ALTERNATE RECORD KEY IS ACCT-CUST-NUMBER
                   WITH DUPLICATES
               FILE STATUS IS WS-ACCT-STATUS.

       DATA DIVISION.
       FILE SECTION.
       FD  CUST-FILE.
       COPY 'capstone-customer.cpy'.

       FD  ACCT-FILE.
       COPY 'capstone-account.cpy'.

       WORKING-STORAGE SECTION.

       COPY 'capstone-constants.cpy'.

       01  WS-ACCT-TYPE-DISPLAY    PIC X(12) VALUE SPACES.
       01  WS-ACCT-STAT-DISPLAY    PIC X(10) VALUE SPACES.
       01  WS-CUST-STAT-DISPLAY    PIC X(10) VALUE SPACES.
       01  WS-EDIT-BALANCE         PIC $$$,$$$,$$$,$$9.99-.
       01  WS-EDIT-RATE            PIC 9.9999.
       01  WS-ACCT-COUNT           PIC 9(03) VALUE ZERO.
       01  WS-TOTAL-BALANCE        PIC S9(13)V99 COMP-3
                                   VALUE ZERO.
       01  WS-EDIT-TOTAL           PIC $$$,$$$,MATH3$,$$9.99-.

       LINKAGE SECTION.
       01  LS-COMM-AREA.
           05  LS-FUNCTION         PIC X(04).
           05  LS-RETURN-CODE      PIC S9(4) COMP.
           05  LS-REASON-CODE      PIC S9(4) COMP.
           05  LS-ERROR-MSG        PIC X(80).
           05  LS-ACCT-NUMBER      PIC X(10).
           05  LS-CUST-NUMBER      PIC X(10).
           05  LS-AMOUNT           PIC S9(11)V99 COMP-3.
           05  LS-TARGET-ACCT      PIC X(10).
           05  LS-PROCESS-DATE     PIC X(10).
           05  LS-USER-ID          PIC X(08).
           05  LS-TIMESTAMP        PIC X(26).

       01  LS-DATA-RECORD          PIC X(500).

       PROCEDURE DIVISION USING LS-COMM-AREA LS-DATA-RECORD.

       0000-MAIN-CONTROL.
           MOVE RC-SUCCESS TO LS-RETURN-CODE
           IF LS-ACCT-NUMBER NOT = SPACES
               PERFORM 1000-ACCOUNT-INQUIRY
           ELSE
               IF LS-CUST-NUMBER NOT = SPACES
                   PERFORM 2000-CUSTOMER-INQUIRY
               ELSE
                   MOVE RC-ERROR TO LS-RETURN-CODE
                   MOVE 'No account or customer specified.'
                       TO LS-ERROR-MSG
               END-IF
           END-IF
           GOBACK
           .

       1000-ACCOUNT-INQUIRY.
           OPEN INPUT ACCT-FILE
           IF WS-ACCT-STATUS NOT = '00'
               MOVE RC-FATAL TO LS-RETURN-CODE
               MOVE 'Cannot open account file.'
                   TO LS-ERROR-MSG
               GO TO 1000-EXIT
           END-IF
           MOVE LS-ACCT-NUMBER TO ACCT-NUMBER
           READ ACCT-FILE
               KEY IS ACCT-NUMBER
           IF WS-ACCT-STATUS NOT = '00'
               MOVE RC-ERROR TO LS-RETURN-CODE
               MOVE 'Account not found.' TO LS-ERROR-MSG
               CLOSE ACCT-FILE
               GO TO 1000-EXIT
           END-IF
           PERFORM 1100-DISPLAY-ACCOUNT-DETAIL
           CLOSE ACCT-FILE
           .
       1000-EXIT.
           EXIT.

       1100-DISPLAY-ACCOUNT-DETAIL.
           PERFORM 1150-RESOLVE-ACCT-TYPE
           PERFORM 1160-RESOLVE-ACCT-STATUS
           DISPLAY '========================================='
           DISPLAY '  ACCOUNT INQUIRY'
           DISPLAY '========================================='
           DISPLAY '  Account No:   ' ACCT-NUMBER
           DISPLAY '  Customer No:  ' ACCT-CUST-NUMBER
           DISPLAY '  Account Type: ' WS-ACCT-TYPE-DISPLAY
           DISPLAY '  Status:       ' WS-ACCT-STAT-DISPLAY
           DISPLAY '  Opened:       ' ACCT-OPEN-DATE
           MOVE ACCT-CURRENT-BALANCE TO WS-EDIT-BALANCE
           DISPLAY '  Balance:      ' WS-EDIT-BALANCE
           MOVE ACCT-AVAILABLE-BALANCE TO WS-EDIT-BALANCE
           DISPLAY '  Available:    ' WS-EDIT-BALANCE
           MOVE ACCT-INTEREST-RATE TO WS-EDIT-RATE
           DISPLAY '  Interest Rate:' WS-EDIT-RATE '%'
           MOVE ACCT-YTD-INTEREST TO WS-EDIT-BALANCE
           DISPLAY '  YTD Interest: ' WS-EDIT-BALANCE
           DISPLAY '  Last Activity:' ACCT-LAST-TXN-DATE
           DISPLAY '========================================='
           .

       1150-RESOLVE-ACCT-TYPE.
           EVALUATE ACCT-TYPE
               WHEN 'C'
                   MOVE 'Checking' TO WS-ACCT-TYPE-DISPLAY
               WHEN 'S'
                   MOVE 'Savings' TO WS-ACCT-TYPE-DISPLAY
               WHEN 'M'
                   MOVE 'Money Market' TO WS-ACCT-TYPE-DISPLAY
               WHEN 'D'
                   MOVE 'Certificate' TO WS-ACCT-TYPE-DISPLAY
               WHEN 'L'
                   MOVE 'Loan' TO WS-ACCT-TYPE-DISPLAY
               WHEN OTHER
                   MOVE 'Unknown' TO WS-ACCT-TYPE-DISPLAY
           END-EVALUATE
           .

       1160-RESOLVE-ACCT-STATUS.
           EVALUATE TRUE
               WHEN ACCT-IS-ACTIVE
                   MOVE 'Active' TO WS-ACCT-STAT-DISPLAY
               WHEN ACCT-IS-CLOSED
                   MOVE 'Closed' TO WS-ACCT-STAT-DISPLAY
               WHEN ACCT-IS-FROZEN
                   MOVE 'Frozen' TO WS-ACCT-STAT-DISPLAY
               WHEN ACCT-IS-DORMANT
                   MOVE 'Dormant' TO WS-ACCT-STAT-DISPLAY
               WHEN OTHER
                   MOVE 'Unknown' TO WS-ACCT-STAT-DISPLAY
           END-EVALUATE
           .

       2000-CUSTOMER-INQUIRY.
           OPEN INPUT CUST-FILE
           IF WS-CUST-STATUS NOT = '00'
               MOVE RC-FATAL TO LS-RETURN-CODE
               MOVE 'Cannot open customer file.'
                   TO LS-ERROR-MSG
               GO TO 2000-EXIT
           END-IF
           MOVE LS-CUST-NUMBER TO CUST-NUMBER
           READ CUST-FILE
               KEY IS CUST-NUMBER
           IF WS-CUST-STATUS NOT = '00'
               MOVE RC-ERROR TO LS-RETURN-CODE
               MOVE 'Customer not found.' TO LS-ERROR-MSG
               CLOSE CUST-FILE
               GO TO 2000-EXIT
           END-IF
           PERFORM 2100-DISPLAY-CUSTOMER-DETAIL
           CLOSE CUST-FILE
           PERFORM 2200-LIST-CUSTOMER-ACCOUNTS
           .
       2000-EXIT.
           EXIT.

       2100-DISPLAY-CUSTOMER-DETAIL.
           EVALUATE TRUE
               WHEN CUST-STAT-ACTIVE
                   MOVE 'Active' TO WS-CUST-STAT-DISPLAY
               WHEN CUST-STAT-INACTIVE
                   MOVE 'Inactive' TO WS-CUST-STAT-DISPLAY
               WHEN CUST-STAT-CLOSED
                   MOVE 'Closed' TO WS-CUST-STAT-DISPLAY
               WHEN OTHER
                   MOVE 'Unknown' TO WS-CUST-STAT-DISPLAY
           END-EVALUATE
           STRING CUST-LAST-NAME DELIMITED BY '  '
                  ', '            DELIMITED BY SIZE
                  CUST-FIRST-NAME DELIMITED BY '  '
                  ' '             DELIMITED BY SIZE
                  CUST-MIDDLE-INIT DELIMITED BY SIZE
                  '.'             DELIMITED BY SIZE
                  INTO CUST-FORMATTED-NAME
           DISPLAY '========================================='
           DISPLAY '  CUSTOMER INQUIRY'
           DISPLAY '========================================='
           DISPLAY '  Customer No:  ' CUST-NUMBER
           DISPLAY '  Name:         ' CUST-FORMATTED-NAME
           DISPLAY '  Status:       ' WS-CUST-STAT-DISPLAY
           DISPLAY '  Address:      ' CUST-ADDR-LINE1
           IF CUST-ADDR-LINE2 NOT = SPACES
               DISPLAY '                ' CUST-ADDR-LINE2
           END-IF
           DISPLAY '                ' CUST-CITY ', '
                   CUST-STATE ' ' CUST-ZIP
           DISPLAY '  Phone:        ' CUST-PHONE-PRIMARY
           DISPLAY '  Email:        ' CUST-EMAIL
           DISPLAY '  Opened:       ' CUST-OPEN-DATE
           DISPLAY '  Accounts:     ' CUST-NUM-ACCOUNTS
           DISPLAY '========================================='
           .

       2200-LIST-CUSTOMER-ACCOUNTS.
           OPEN INPUT ACCT-FILE
           IF WS-ACCT-STATUS NOT = '00'
               DISPLAY '  (Unable to list accounts)'
               GO TO 2200-EXIT
           END-IF
           MOVE ZERO TO WS-ACCT-COUNT
           MOVE ZERO TO WS-TOTAL-BALANCE
           MOVE LS-CUST-NUMBER TO ACCT-CUST-NUMBER
           START ACCT-FILE
               KEY IS EQUAL TO ACCT-CUST-NUMBER
           IF WS-ACCT-STATUS NOT = '00'
               DISPLAY '  No accounts found.'
               CLOSE ACCT-FILE
               GO TO 2200-EXIT
           END-IF
           DISPLAY '  --- Account Summary ---'
           DISPLAY '  Acct Number  Type         Balance'
           DISPLAY '  ----------  -----------  --------'
           PERFORM 2210-READ-NEXT-ACCOUNT
               UNTIL WS-ACCT-STATUS NOT = '00'
           MOVE WS-TOTAL-BALANCE TO WS-EDIT-TOTAL
           DISPLAY '  ----------  -----------  --------'
           DISPLAY '  Total (' WS-ACCT-COUNT
                   ' accounts):   ' WS-EDIT-TOTAL
           CLOSE ACCT-FILE
           .
       2200-EXIT.
           EXIT.

       2210-READ-NEXT-ACCOUNT.
           READ ACCT-FILE NEXT RECORD
           IF WS-ACCT-STATUS = '00'
               IF ACCT-CUST-NUMBER = LS-CUST-NUMBER
                   ADD 1 TO WS-ACCT-COUNT
                   ADD ACCT-CURRENT-BALANCE
                       TO WS-TOTAL-BALANCE
                   PERFORM 1150-RESOLVE-ACCT-TYPE
                   MOVE ACCT-CURRENT-BALANCE
                       TO WS-EDIT-BALANCE
                   DISPLAY '  ' ACCT-NUMBER '  '
                       WS-ACCT-TYPE-DISPLAY '  '
                       WS-EDIT-BALANCE
               ELSE
                   MOVE '10' TO WS-ACCT-STATUS
               END-IF
           END-IF
           .
```

### Noteworthy Patterns in CAPINQRY

The inquiry program uses **DYNAMIC access mode** on the account file (Chapter 12). This allows it to perform a random READ by primary key for a specific account inquiry, and also to perform a sequential START/READ NEXT by alternate key to list all accounts belonging to a customer. Dynamic access is essential whenever a program needs both random and sequential access to the same VSAM file within a single execution.

The customer name formatting in paragraph 2100 uses the STRING verb (Chapter 9) to assemble the display name from its component fields. The DELIMITED BY '  ' (two spaces) clause trims trailing spaces from the variable-length name fields, producing clean output like "Smith, John A." instead of "Smith                          , John                A.".

The EVALUATE TRUE pattern in paragraphs 1160 and 2100 tests 88-level condition names directly. This is one of the most powerful and readable patterns in COBOL: rather than evaluating a data item against a series of literals, you evaluate the boolean truth of condition names that carry their own documentation. This pattern was covered in Chapter 7 (Conditional Logic and the EVALUATE Statement).

---

## 41.7 Building the Interest Calculation Batch Program (CAPINTRN)

The interest calculation program runs as a nightly batch job. It reads every active account from the VSAM file sequentially, calculates the daily interest accrual based on the account type and balance, updates the accrued interest fields, and writes a summary report. This program demonstrates sequential processing of an indexed file (Chapter 12), compound arithmetic (Chapter 6, Chapter 33), and batch processing patterns (Chapter 28).

```cobol
       IDENTIFICATION DIVISION.
       PROGRAM-ID. CAPINTRN.
      *================================================================*
      * CAPINTRN - Daily Interest Calculation Batch Program            *
      * Processes all active accounts, calculates daily interest       *
      * accrual, and updates account records. Runs as part of the     *
      * nightly batch cycle.                                           *
      *================================================================*

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT ACCT-FILE
               ASSIGN TO ACCTFILE
               ORGANIZATION IS INDEXED
               ACCESS MODE IS SEQUENTIAL
               RECORD KEY IS ACCT-NUMBER
               FILE STATUS IS WS-ACCT-STATUS.
           SELECT REPORT-FILE
               ASSIGN TO INTRPT
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-REPORT-STATUS.

       DATA DIVISION.
       FILE SECTION.
       FD  ACCT-FILE.
       COPY 'capstone-account.cpy'.

       FD  REPORT-FILE
           RECORDING MODE IS F
           RECORD CONTAINS 132 CHARACTERS.
       01  REPORT-RECORD             PIC X(132).

       WORKING-STORAGE SECTION.

       COPY 'capstone-constants.cpy'.

       01  WS-DAILY-RATE           PIC 9V9(10) VALUE ZERO.
       01  WS-ANNUAL-RATE          PIC 9V9(6)  VALUE ZERO.
       01  WS-DAILY-INTEREST       PIC S9(9)V99 COMP-3
                                   VALUE ZERO.
       01  WS-TOTAL-INTEREST       PIC S9(13)V99 COMP-3
                                   VALUE ZERO.
       01  WS-ACCTS-PROCESSED      PIC 9(07)   VALUE ZERO.
       01  WS-ACCTS-SKIPPED        PIC 9(07)   VALUE ZERO.
       01  WS-ACCTS-INTEREST       PIC 9(07)   VALUE ZERO.
       01  WS-EOF-FLAG             PIC X(01)   VALUE 'N'.
           88  END-OF-FILE         VALUE 'Y'.
       01  WS-DAYS-IN-YEAR         PIC 9(03)   VALUE 365.
       01  WS-PAGE-COUNT           PIC 9(03)   VALUE ZERO.
       01  WS-LINE-COUNT           PIC 9(03)   VALUE ZERO.
       01  WS-LINES-PER-PAGE       PIC 9(03)   VALUE 55.

       01  RPT-HEADER-1.
           05  FILLER              PIC X(40)
               VALUE 'COMMUNITY BANK - DAILY INTEREST REPORT'.
           05  FILLER              PIC X(50)   VALUE SPACES.
           05  RPT-H1-DATE        PIC X(10).
           05  FILLER              PIC X(06)   VALUE ' PAGE '.
           05  RPT-H1-PAGE        PIC ZZ9.
           05  FILLER              PIC X(23)   VALUE SPACES.

       01  RPT-HEADER-2.
           05  FILLER              PIC X(10)
               VALUE 'ACCOUNT   '.
           05  FILLER              PIC X(02)   VALUE SPACES.
           05  FILLER              PIC X(12)
               VALUE 'TYPE        '.
           05  FILLER              PIC X(02)   VALUE SPACES.
           05  FILLER              PIC X(20)
               VALUE 'CURRENT BALANCE     '.
           05  FILLER              PIC X(02)   VALUE SPACES.
           05  FILLER              PIC X(10)
               VALUE 'RATE      '.
           05  FILLER              PIC X(02)   VALUE SPACES.
           05  FILLER              PIC X(18)
               VALUE 'DAILY INTEREST    '.
           05  FILLER              PIC X(02)   VALUE SPACES.
           05  FILLER              PIC X(20)
               VALUE 'ACCRUED INTEREST    '.
           05  FILLER              PIC X(32)   VALUE SPACES.

       01  RPT-DETAIL.
           05  RPT-DTL-ACCT       PIC X(10).
           05  FILLER              PIC X(02)   VALUE SPACES.
           05  RPT-DTL-TYPE       PIC X(12).
           05  FILLER              PIC X(02)   VALUE SPACES.
           05  RPT-DTL-BALANCE    PIC $$$,MATH5$,$$9.99-.
           05  FILLER              PIC X(02)   VALUE SPACES.
           05  RPT-DTL-RATE       PIC 9.9999.
           05  FILLER              PIC X(04)   VALUE '%   '.
           05  RPT-DTL-DAILY      PIC $$,$$$,$$9.99-.
           05  FILLER              PIC X(02)   VALUE SPACES.
           05  RPT-DTL-ACCRUED    PIC $$$,$$$,MATH9MATH10$,$$9.99.
           05  FILLER              PIC X(88)   VALUE SPACES.

       PROCEDURE DIVISION.

       0000-MAIN-CONTROL.
           PERFORM 1000-INITIALIZE
           PERFORM 2000-PROCESS-ACCOUNTS
               UNTIL END-OF-FILE
           PERFORM 3000-WRITE-SUMMARY
           PERFORM 9000-TERMINATE
           STOP RUN
           .

       1000-INITIALIZE.
           OPEN I-O ACCT-FILE
           IF WS-ACCT-STATUS NOT = '00'
               DISPLAY 'FATAL: Cannot open account file: '
                   WS-ACCT-STATUS
               MOVE 16 TO RETURN-CODE
               STOP RUN
           END-IF
           OPEN OUTPUT REPORT-FILE
           MOVE FUNCTION CURRENT-DATE(1:4)  TO WS-CURR-YEAR
           MOVE FUNCTION CURRENT-DATE(5:2)  TO WS-CURR-MONTH
           MOVE FUNCTION CURRENT-DATE(7:2)  TO WS-CURR-DAY
           STRING WS-CURR-YEAR '-'
                  WS-CURR-MONTH '-'
                  WS-CURR-DAY
                  DELIMITED BY SIZE
                  INTO RPT-H1-DATE
      *    Check for leap year (divisible by 4, except
      *    centuries unless divisible by 400)
           IF FUNCTION MOD(
                   FUNCTION NUMVAL(WS-CURR-YEAR) 4) = 0
               IF FUNCTION MOD(
                       FUNCTION NUMVAL(WS-CURR-YEAR) 100)
                   NOT = 0
                   MOVE 366 TO WS-DAYS-IN-YEAR
               ELSE
                   IF FUNCTION MOD(
                           FUNCTION NUMVAL(WS-CURR-YEAR) 400)
                       = 0
                       MOVE 366 TO WS-DAYS-IN-YEAR
                   END-IF
               END-IF
           END-IF
           PERFORM 4000-WRITE-PAGE-HEADER
           .

       2000-PROCESS-ACCOUNTS.
           READ ACCT-FILE
           IF WS-ACCT-STATUS = '00'
               ADD 1 TO WS-ACCTS-PROCESSED
               IF ACCT-IS-ACTIVE
                   PERFORM 2100-CALCULATE-INTEREST
               ELSE
                   ADD 1 TO WS-ACCTS-SKIPPED
               END-IF
           ELSE
               SET END-OF-FILE TO TRUE
           END-IF
           .

       2100-CALCULATE-INTEREST.
      *    Determine the annual rate based on account type
           EVALUATE TRUE
               WHEN ACCT-IS-CHECKING
                   MOVE RATE-CHECKING TO WS-ANNUAL-RATE
               WHEN ACCT-IS-SAVINGS
                   MOVE RATE-SAVINGS TO WS-ANNUAL-RATE
               WHEN ACCT-IS-MM
                   MOVE RATE-MONEY-MARKET TO WS-ANNUAL-RATE
               WHEN ACCT-IS-CD
                   MOVE ACCT-INTEREST-RATE TO WS-ANNUAL-RATE
               WHEN OTHER
                   MOVE ZERO TO WS-ANNUAL-RATE
           END-EVALUATE
           IF WS-ANNUAL-RATE > ZERO
               AND ACCT-CURRENT-BALANCE > ZERO
      *        Daily rate = annual rate / days in year
               COMPUTE WS-DAILY-RATE =
                   WS-ANNUAL-RATE / WS-DAYS-IN-YEAR
      *        Daily interest = balance * daily rate
               COMPUTE WS-DAILY-INTEREST ROUNDED =
                   ACCT-CURRENT-BALANCE * WS-DAILY-RATE
               ADD WS-DAILY-INTEREST
                   TO ACCT-ACCRUED-INTEREST
               ADD WS-DAILY-INTEREST
                   TO ACCT-MTD-INTEREST
               ADD WS-DAILY-INTEREST
                   TO ACCT-YTD-INTEREST
               ADD WS-DAILY-INTEREST TO WS-TOTAL-INTEREST
               ADD 1 TO WS-ACCTS-INTEREST
               REWRITE ACCOUNT-RECORD
               IF WS-ACCT-STATUS NOT = '00'
                   DISPLAY 'WARNING: Rewrite failed for '
                       ACCT-NUMBER ' Status: '
                       WS-ACCT-STATUS
               END-IF
               PERFORM 2200-WRITE-DETAIL-LINE
           END-IF
           .

       2200-WRITE-DETAIL-LINE.
           IF WS-LINE-COUNT >= WS-LINES-PER-PAGE
               PERFORM 4000-WRITE-PAGE-HEADER
           END-IF
           MOVE ACCT-NUMBER         TO RPT-DTL-ACCT
           EVALUATE TRUE
               WHEN ACCT-IS-CHECKING
                   MOVE 'Checking'   TO RPT-DTL-TYPE
               WHEN ACCT-IS-SAVINGS
                   MOVE 'Savings'    TO RPT-DTL-TYPE
               WHEN ACCT-IS-MM
                   MOVE 'Money Mkt'  TO RPT-DTL-TYPE
               WHEN ACCT-IS-CD
                   MOVE 'CD'         TO RPT-DTL-TYPE
               WHEN OTHER
                   MOVE 'Other'      TO RPT-DTL-TYPE
           END-EVALUATE
           MOVE ACCT-CURRENT-BALANCE TO RPT-DTL-BALANCE
           MOVE WS-ANNUAL-RATE       TO RPT-DTL-RATE
           MOVE WS-DAILY-INTEREST    TO RPT-DTL-DAILY
           MOVE ACCT-ACCRUED-INTEREST TO RPT-DTL-ACCRUED
           WRITE REPORT-RECORD FROM RPT-DETAIL
           ADD 1 TO WS-LINE-COUNT
           .

       3000-WRITE-SUMMARY.
           WRITE REPORT-RECORD FROM RPT-FOOTER
           MOVE WS-ACCTS-PROCESSED TO RPT-SUM-PROC
           WRITE REPORT-RECORD FROM RPT-SUMMARY-1
           MOVE WS-ACCTS-INTEREST  TO RPT-SUM-INTR
           WRITE REPORT-RECORD FROM RPT-SUMMARY-2
           MOVE WS-TOTAL-INTEREST  TO RPT-SUM-TOTAL
           WRITE REPORT-RECORD FROM RPT-SUMMARY-3
           .

       4000-WRITE-PAGE-HEADER.
           ADD 1 TO WS-PAGE-COUNT
           MOVE WS-PAGE-COUNT TO RPT-H1-PAGE
           IF WS-PAGE-COUNT > 1
               WRITE REPORT-RECORD FROM SPACES
                   AFTER ADVANCING PAGE
           END-IF
           WRITE REPORT-RECORD FROM RPT-HEADER-1
           WRITE REPORT-RECORD FROM SPACES
           WRITE REPORT-RECORD FROM RPT-HEADER-2
           MOVE ALL '-' TO REPORT-RECORD
           WRITE REPORT-RECORD
           MOVE 4 TO WS-LINE-COUNT
           .

       9000-TERMINATE.
           CLOSE ACCT-FILE
           CLOSE REPORT-FILE
           DISPLAY 'CAPINTRN: Processing complete.'
           DISPLAY '  Accounts processed: '
               WS-ACCTS-PROCESSED
           DISPLAY '  Accounts with interest: '
               WS-ACCTS-INTEREST
           DISPLAY '  Total daily interest: '
               WS-TOTAL-INTEREST
           MOVE ZERO TO RETURN-CODE
           .
```

### Financial Calculation Details

The interest calculation in paragraph 2100-CALCULATE-INTEREST implements the **simple daily accrual method**, which is the most common method used by U.S. banks for deposit accounts. The daily interest is calculated as:

```
Daily Interest = Balance * (Annual Rate / Days in Year)
```

The program correctly determines the number of days in the year (365 or 366) using the leap year algorithm from Chapter 33 (Financial Calculations). The COMPUTE statement with the ROUNDED phrase (Chapter 6) ensures that interest amounts are rounded to the nearest cent rather than truncated, which is a regulatory requirement.

For certificates of deposit, the program uses the account-specific rate stored in ACCT-INTEREST-RATE rather than the standard rate table. This allows CD rates to be locked at the time of purchase, a fundamental feature of CD products.

The interest is accumulated in three fields: ACCT-ACCRUED-INTEREST (unpaid interest since the last posting), ACCT-MTD-INTEREST (month-to-date for statement reporting), and ACCT-YTD-INTEREST (year-to-date for 1099-INT reporting). This three-tier accumulation structure is standard in banking systems and supports the separate business processes of interest posting (monthly), statement generation (monthly), and tax reporting (annual).

---

## 41.8 Building the Daily Report Program (CAPRPORT)

The reporting program generates the daily transaction summary and a Currency Transaction Report (CTR) for transactions exceeding the $10,000 threshold. It uses the SORT verb with an INPUT PROCEDURE and OUTPUT PROCEDURE (Chapter 14) to order transactions by account and date before generating the report.

```cobol
       IDENTIFICATION DIVISION.
       PROGRAM-ID. CAPRPORT.
      *================================================================*
      * CAPRPORT - Daily Transaction Summary and Regulatory Reports    *
      * Reads the daily transaction file, sorts by account and date,   *
      * generates a summary report and a Currency Transaction Report   *
      * for transactions exceeding the CTR threshold ($10,000).        *
      *================================================================*

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT TXN-INPUT-FILE
               ASSIGN TO TXNINPUT
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-TXN-STATUS.
           SELECT SORT-FILE
               ASSIGN TO SORTWORK.
           SELECT REPORT-FILE
               ASSIGN TO RPTFILE
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-REPORT-STATUS.
           SELECT CTR-FILE
               ASSIGN TO CTRFILE
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-STMT-STATUS.

       DATA DIVISION.
       FILE SECTION.

       FD  TXN-INPUT-FILE
           RECORDING MODE IS F
           RECORD CONTAINS 300 CHARACTERS.
       COPY 'capstone-transaction.cpy'.

       SD  SORT-FILE
           RECORD CONTAINS 300 CHARACTERS.
       01  SORT-RECORD.
           05  SORT-KEY.
               10  SORT-ACCT      PIC X(10).
               10  SORT-DATE      PIC X(10).
               10  SORT-TIME      PIC X(08).
           05  SORT-DATA           PIC X(272).

       FD  REPORT-FILE
           RECORDING MODE IS F
           RECORD CONTAINS 132 CHARACTERS.
       01  REPORT-RECORD           PIC X(132).

       FD  CTR-FILE
           RECORDING MODE IS F
           RECORD CONTAINS 132 CHARACTERS.
       01  CTR-RECORD              PIC X(132).

       WORKING-STORAGE SECTION.

       COPY 'capstone-constants.cpy'.

       01  WS-TXN-WORK-REC.
           05  WS-TXN-W-ACCT      PIC X(10).
           05  WS-TXN-W-TYPE      PIC X(02).
           05  WS-TXN-W-AMOUNT    PIC S9(11)V99 COMP-3.
           05  WS-TXN-W-DATE      PIC X(10).
           05  WS-TXN-W-DESC      PIC X(40).
           05  WS-TXN-W-STATUS    PIC X(01).
           05  WS-TXN-W-CHANNEL   PIC X(02).
           05  WS-TXN-W-FILLER    PIC X(218).

       01  WS-EOF-FLAG             PIC X(01) VALUE 'N'.
           88  END-OF-FILE         VALUE 'Y'.
       01  WS-SORT-EOF             PIC X(01) VALUE 'N'.
           88  SORT-END-OF-FILE    VALUE 'Y'.

       01  WS-PREV-ACCT           PIC X(10) VALUE SPACES.

       01  WS-RPT-LINE-CT         PIC 9(03) VALUE ZERO.
       01  WS-RPT-PAGE-CT         PIC 9(03) VALUE ZERO.
       01  WS-CTR-COUNT           PIC 9(05) VALUE ZERO.

       01  WS-EDIT-AMT            PIC $$$,MATH12$,$$9.99-.
       01  WS-TYPE-DESC           PIC X(12) VALUE SPACES.

       01  WS-RPT-HDR.
           05  FILLER              PIC X(45)
               VALUE 'COMMUNITY BANK - DAILY TRANSACTION SUMMARY'.
           05  FILLER              PIC X(45) VALUE SPACES.
           05  WS-RPT-HDR-DATE    PIC X(10).
           05  FILLER              PIC X(06) VALUE ' PAGE '.
           05  WS-RPT-HDR-PAGE    PIC ZZ9.
           05  FILLER              PIC X(23) VALUE SPACES.

       01  WS-RPT-COL-HDR.
           05  FILLER PIC X(10) VALUE 'ACCOUNT   '.
           05  FILLER PIC X(02) VALUE SPACES.
           05  FILLER PIC X(10) VALUE 'DATE      '.
           05  FILLER PIC X(02) VALUE SPACES.
           05  FILLER PIC X(12) VALUE 'TYPE        '.
           05  FILLER PIC X(02) VALUE SPACES.
           05  FILLER PIC X(20) VALUE 'AMOUNT              '.
           05  FILLER PIC X(02) VALUE SPACES.
           05  FILLER PIC X(02) VALUE 'CH'.
           05  FILLER PIC X(02) VALUE SPACES.
           05  FILLER PIC X(01) VALUE 'S'.
           05  FILLER PIC X(67) VALUE SPACES.

       01  WS-RPT-DTL.
           05  WS-RPT-D-ACCT      PIC X(10).
           05  FILLER              PIC X(02) VALUE SPACES.
           05  WS-RPT-D-DATE      PIC X(10).
           05  FILLER              PIC X(02) VALUE SPACES.
           05  WS-RPT-D-TYPE      PIC X(12).
           05  FILLER              PIC X(02) VALUE SPACES.
           05  WS-RPT-D-AMOUNT    PIC $$$,MATH14$,$$9.99-.
           05  FILLER              PIC X(02) VALUE SPACES.
           05  WS-RPT-D-CHAN      PIC X(02).
           05  FILLER              PIC X(02) VALUE SPACES.
           05  WS-RPT-D-STAT      PIC X(01).
           05  FILLER              PIC X(67) VALUE SPACES.

       01  WS-CTR-HDR.
           05  FILLER              PIC X(50)
               VALUE 'CURRENCY TRANSACTION REPORT (CTR) - TRANSACTIONS'.
           05  FILLER              PIC X(20)
               VALUE ' EXCEEDING $10,000  '.
           05  FILLER              PIC X(62) VALUE SPACES.

       01  WS-CTR-DTL.
           05  WS-CTR-D-ACCT      PIC X(10).
           05  FILLER              PIC X(02) VALUE SPACES.
           05  WS-CTR-D-DATE      PIC X(10).
           05  FILLER              PIC X(02) VALUE SPACES.
           05  WS-CTR-D-TYPE      PIC X(12).
           05  FILLER              PIC X(02) VALUE SPACES.
           05  WS-CTR-D-AMOUNT    PIC $$$,MATH16$,$$9.99-.
           05  FILLER              PIC X(02) VALUE SPACES.
           05  WS-CTR-D-CHAN      PIC X(02).
           05  FILLER              PIC X(02) VALUE SPACES.
           05  WS-CTR-D-DESC     PIC X(40).
           05  FILLER              PIC X(28) VALUE SPACES.

       PROCEDURE DIVISION.

       0000-MAIN-CONTROL.
           OPEN OUTPUT REPORT-FILE
           OPEN OUTPUT CTR-FILE
           MOVE FUNCTION CURRENT-DATE(1:4)  TO WS-CURR-YEAR
           MOVE FUNCTION CURRENT-DATE(5:2)  TO WS-CURR-MONTH
           MOVE FUNCTION CURRENT-DATE(7:2)  TO WS-CURR-DAY
           STRING WS-CURR-YEAR '-' WS-CURR-MONTH
                  '-' WS-CURR-DAY
                  DELIMITED BY SIZE
                  INTO WS-RPT-HDR-DATE
           WRITE CTR-RECORD FROM WS-CTR-HDR
           WRITE CTR-RECORD FROM SPACES
           SORT SORT-FILE
               ON ASCENDING KEY SORT-ACCT
               ON ASCENDING KEY SORT-DATE
               ON ASCENDING KEY SORT-TIME
               INPUT PROCEDURE IS 1000-INPUT-PROC
               OUTPUT PROCEDURE IS 2000-OUTPUT-PROC
           PERFORM 3000-WRITE-TOTALS
           CLOSE REPORT-FILE
           CLOSE CTR-FILE
           DISPLAY 'CAPRPORT: Report generation complete.'
           DISPLAY '  CTR filings: ' WS-CTR-COUNT
           MOVE ZERO TO RETURN-CODE
           STOP RUN
           .

       1000-INPUT-PROC SECTION.
           OPEN INPUT TXN-INPUT-FILE
           IF WS-TXN-STATUS NOT = '00'
               DISPLAY 'FATAL: Cannot open txn input: '
                   WS-TXN-STATUS
               MOVE 16 TO RETURN-CODE
               STOP RUN
           END-IF
           PERFORM 1100-READ-AND-RELEASE
               UNTIL END-OF-FILE
           CLOSE TXN-INPUT-FILE
           .

       1100-READ-AND-RELEASE.
           READ TXN-INPUT-FILE
           IF WS-TXN-STATUS = '00'
               ADD 1 TO CTR-RECORDS-READ
      *        Check for CTR threshold ($10,000)
               IF TXN-AMOUNT >= LIMIT-CTR-THRESHOLD
                   PERFORM 1200-WRITE-CTR-RECORD
               END-IF
               MOVE TRANSACTION-RECORD TO SORT-RECORD
               RELEASE SORT-RECORD
           ELSE
               SET END-OF-FILE TO TRUE
           END-IF
           .

       1200-WRITE-CTR-RECORD.
           ADD 1 TO WS-CTR-COUNT
           MOVE TXN-ACCT-NUMBER    TO WS-CTR-D-ACCT
           MOVE TXN-DATE           TO WS-CTR-D-DATE
           PERFORM 8000-RESOLVE-TXN-TYPE
           MOVE WS-TYPE-DESC       TO WS-CTR-D-TYPE
           MOVE TXN-AMOUNT         TO WS-CTR-D-AMOUNT
           MOVE TXN-CHANNEL        TO WS-CTR-D-CHAN
           MOVE TXN-DESCRIPTION    TO WS-CTR-D-DESC
           WRITE CTR-RECORD FROM WS-CTR-DTL
           .

       2000-OUTPUT-PROC SECTION.
           PERFORM 2050-WRITE-PAGE-HEADER
           PERFORM 2100-RETURN-AND-REPORT
               UNTIL SORT-END-OF-FILE
           .

       2050-WRITE-PAGE-HEADER.
           ADD 1 TO WS-RPT-PAGE-CT
           MOVE WS-RPT-PAGE-CT TO WS-RPT-HDR-PAGE
           IF WS-RPT-PAGE-CT > 1
               WRITE REPORT-RECORD FROM SPACES
                   AFTER ADVANCING PAGE
           END-IF
           WRITE REPORT-RECORD FROM WS-RPT-HDR
           WRITE REPORT-RECORD FROM SPACES
           WRITE REPORT-RECORD FROM WS-RPT-COL-HDR
           MOVE ALL '-' TO REPORT-RECORD
           WRITE REPORT-RECORD
           MOVE 4 TO WS-RPT-LINE-CT
           .

       2100-RETURN-AND-REPORT.
           RETURN SORT-FILE INTO TRANSACTION-RECORD
               AT END
                   SET SORT-END-OF-FILE TO TRUE
               NOT AT END
                   ADD 1 TO CTR-TOTAL-PROCESSED
                   PERFORM 2200-WRITE-DETAIL
           END-RETURN
           .

       2200-WRITE-DETAIL.
           IF WS-RPT-LINE-CT >= 55
               PERFORM 2050-WRITE-PAGE-HEADER
           END-IF
           MOVE TXN-ACCT-NUMBER  TO WS-RPT-D-ACCT
           MOVE TXN-DATE         TO WS-RPT-D-DATE
           PERFORM 8000-RESOLVE-TXN-TYPE
           MOVE WS-TYPE-DESC     TO WS-RPT-D-TYPE
           MOVE TXN-AMOUNT       TO WS-RPT-D-AMOUNT
           MOVE TXN-CHANNEL      TO WS-RPT-D-CHAN
           MOVE TXN-STATUS       TO WS-RPT-D-STAT
           WRITE REPORT-RECORD FROM WS-RPT-DTL
           ADD 1 TO WS-RPT-LINE-CT
           EVALUATE TXN-TYPE
               WHEN 'DP'
                   ADD TXN-AMOUNT TO TXN-SUM-DEPOSITS
               WHEN 'WD'
                   ADD TXN-AMOUNT TO TXN-SUM-WITHDRAWALS
               WHEN 'XF'
                   ADD TXN-AMOUNT TO TXN-SUM-TRANSFERS-OUT
               WHEN 'IN'
                   ADD TXN-AMOUNT TO TXN-SUM-INTEREST
               WHEN 'FE'
                   ADD TXN-AMOUNT TO TXN-SUM-FEES
           END-EVALUATE
           ADD 1 TO TXN-SUM-COUNT
           .

       3000-WRITE-TOTALS.
           WRITE REPORT-RECORD FROM SPACES
           MOVE ALL '=' TO REPORT-RECORD
           WRITE REPORT-RECORD
           MOVE SPACES TO REPORT-RECORD
           STRING 'Total Records Read:      '
                  DELIMITED BY SIZE INTO REPORT-RECORD
           WRITE REPORT-RECORD
           MOVE SPACES TO REPORT-RECORD
           STRING 'Total Records Processed: '
                  DELIMITED BY SIZE INTO REPORT-RECORD
           WRITE REPORT-RECORD
           MOVE TXN-SUM-DEPOSITS TO WS-EDIT-AMT
           MOVE SPACES TO REPORT-RECORD
           STRING 'Total Deposits:     ' WS-EDIT-AMT
                  DELIMITED BY SIZE INTO REPORT-RECORD
           WRITE REPORT-RECORD
           MOVE TXN-SUM-WITHDRAWALS TO WS-EDIT-AMT
           MOVE SPACES TO REPORT-RECORD
           STRING 'Total Withdrawals:  ' WS-EDIT-AMT
                  DELIMITED BY SIZE INTO REPORT-RECORD
           WRITE REPORT-RECORD
           MOVE TXN-SUM-FEES TO WS-EDIT-AMT
           MOVE SPACES TO REPORT-RECORD
           STRING 'Total Fees:         ' WS-EDIT-AMT
                  DELIMITED BY SIZE INTO REPORT-RECORD
           WRITE REPORT-RECORD
           .

       8000-RESOLVE-TXN-TYPE.
           EVALUATE TXN-TYPE
               WHEN 'DP'
                   MOVE 'Deposit'     TO WS-TYPE-DESC
               WHEN 'WD'
                   MOVE 'Withdrawal'  TO WS-TYPE-DESC
               WHEN 'XF'
                   MOVE 'Transfer'    TO WS-TYPE-DESC
               WHEN 'IN'
                   MOVE 'Interest'    TO WS-TYPE-DESC
               WHEN 'FE'
                   MOVE 'Fee/Charge'  TO WS-TYPE-DESC
               WHEN 'AJ'
                   MOVE 'Adjustment'  TO WS-TYPE-DESC
               WHEN 'RV'
                   MOVE 'Reversal'    TO WS-TYPE-DESC
               WHEN OTHER
                   MOVE 'Unknown'     TO WS-TYPE-DESC
           END-EVALUATE
           .

SORT with INPUT and OUTPUT PROCEDURE

This program showcases one of the most powerful features covered in Chapter 14 (SORT and MERGE). The SORT statement with INPUT PROCEDURE and OUTPUT PROCEDURE gives the programmer complete control over the sort process:

  • The INPUT PROCEDURE (1000-INPUT-PROC) reads the sequential transaction file, checks each record against the CTR threshold and writes a CTR record when appropriate, then RELEASEs the record to the sort work file. This is more efficient than sorting first and scanning afterward, because the CTR check happens during the single input pass.

  • The OUTPUT PROCEDURE (2000-OUTPUT-PROC) RETURNs sorted records from the sort work file one at a time and writes them to the report. Because the records are sorted by account and date, the report naturally groups transactions by account in chronological order.

The sort key consists of three fields: account number, date, and time. This produces a report where all transactions for a given account appear together, ordered chronologically -- exactly what an auditor or account officer needs to see.


41.9 JCL for the Batch Processing Cycle

The batch programs (CAPINTRN, CAPSTMNT, and CAPRPORT) run as part of the nightly batch cycle, orchestrated by JCL (Chapter 27). The following JCL job stream runs all three programs in sequence, with each step conditional on the success of the previous step:

//CAPNIGHT JOB (ACCT),'COMMUNITY BANK',
//         CLASS=A,MSGCLASS=X,MSGLEVEL=(1,1),
//         NOTIFY=&SYSUID
//*================================================================*
//* COMMUNITY BANK - NIGHTLY BATCH PROCESSING                      *
//* STEP 1: Daily interest calculation (CAPINTRN)                  *
//* STEP 2: Statement generation (CAPSTMNT)                        *
//* STEP 3: Transaction reports (CAPRPORT)                         *
//*================================================================*
//*
//*----------------------------------------------------------------*
//* STEP 1: DAILY INTEREST CALCULATION                             *
//*----------------------------------------------------------------*
//INTEREST EXEC PGM=CAPINTRN
//STEPLIB  DD DSN=CAPSTONE.LOAD.LIBRARY,DISP=SHR
//ACCTFILE DD DSN=CAPSTONE.ACCT.MASTER,DISP=SHR
//INTRPT   DD DSN=CAPSTONE.REPORTS.INTEREST,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(5,2)),
//            DCB=(RECFM=FB,LRECL=132,BLKSIZE=0)
//SYSOUT   DD SYSOUT=*
//*
//*----------------------------------------------------------------*
//* STEP 2: MONTHLY STATEMENT GENERATION                           *
//*         Runs only if interest calc succeeded (RC=0)            *
//*----------------------------------------------------------------*
//STMTGEN  EXEC PGM=CAPSTMNT,COND=(4,LT,INTEREST)
//STEPLIB  DD DSN=CAPSTONE.LOAD.LIBRARY,DISP=SHR
//ACCTFILE DD DSN=CAPSTONE.ACCT.MASTER,DISP=SHR
//CUSTFILE DD DSN=CAPSTONE.CUST.MASTER,DISP=SHR
//TXNHFILE DD DSN=CAPSTONE.TXN.HISTORY,DISP=SHR
//STMTFILE DD DSN=CAPSTONE.STATEMENTS.DAILY,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(10,5)),
//            DCB=(RECFM=FB,LRECL=132,BLKSIZE=0)
//SYSOUT   DD SYSOUT=*
//*
//*----------------------------------------------------------------*
//* STEP 3: DAILY TRANSACTION REPORTS                              *
//*         Runs only if statement gen succeeded (RC<=4)           *
//*----------------------------------------------------------------*
//REPORTS  EXEC PGM=CAPRPORT,COND=(4,LT,STMTGEN)
//STEPLIB  DD DSN=CAPSTONE.LOAD.LIBRARY,DISP=SHR
//TXNINPUT DD DSN=CAPSTONE.TXN.DAILY.INPUT,DISP=SHR
//SORTWORK DD DSN=&&SORTWORK,DISP=(NEW,DELETE),
//            SPACE=(CYL,(10,5))
//RPTFILE  DD DSN=CAPSTONE.REPORTS.DAILY.SUMMARY,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(5,2)),
//            DCB=(RECFM=FB,LRECL=132,BLKSIZE=0)
//CTRFILE  DD DSN=CAPSTONE.REPORTS.CTR.DAILY,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(1,1)),
//            DCB=(RECFM=FB,LRECL=132,BLKSIZE=0)
//SYSOUT   DD SYSOUT=*

JCL Design Notes

The COND parameter on steps STMTGEN and REPORTS implements step-level condition checking (Chapter 27). The syntax COND=(4,LT,INTEREST) means "skip this step if 4 is less than the return code of the INTEREST step" -- in other words, skip if the INTEREST step returned a code greater than 4. This ensures that a failed interest calculation does not lead to incorrect statements or reports.

The SORTWORK DD statement in the REPORTS step uses a temporary data set (&&SORTWORK) that exists only for the duration of the job step. The SORT verb in CAPRPORT uses this data set as its work area for the sort operation. Allocating sort work space in JCL rather than letting the sort utility determine its own allocation gives the operations team control over space management (Chapter 27, Chapter 29).

The STEPLIB DD statement in each step points to the load library containing the compiled COBOL programs. In a production environment, this library would be managed by a change management tool like Endevor or ChangeMan, ensuring that only tested and approved program versions are executed in production (Chapter 40).

Dataset naming follows the standard convention introduced in Chapter 30 (z/OS Dataset Concepts): the high-level qualifier identifies the application (CAPSTONE), followed by a descriptor of the data type, followed by a specific qualifier. This makes datasets easy to identify and manage.


41.10 The Validation Subprogram (CAPVALID)

Robust input validation is critical in any financial system. Rather than scattering validation logic across every program, the Community Bank application centralizes common validation routines in a single subprogram. This is the reusability principle from Chapter 17 taken to its logical conclusion: write once, call from everywhere.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. CAPVALID.
      *================================================================*
      * CAPVALID - Centralized Validation Routines                     *
      * Validates account numbers, amounts, dates, and customer data.  *
      * Returns a status code and error message for each validation.   *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       COPY 'capstone-constants.cpy'.

       01  WS-VALID-WORK           PIC X(80)   VALUE SPACES.
       01  WS-NUM-TEST             PIC X(10)   VALUE SPACES.
       01  WS-DATE-YEAR            PIC 9(04)   VALUE ZERO.
       01  WS-DATE-MONTH           PIC 9(02)   VALUE ZERO.
       01  WS-DATE-DAY             PIC 9(02)   VALUE ZERO.
       01  WS-LEAP-YEAR-FLAG       PIC X(01)   VALUE 'N'.
           88  IS-LEAP-YEAR        VALUE 'Y'.
       01  WS-MAX-DAY              PIC 9(02)   VALUE ZERO.

       LINKAGE SECTION.
       01  LS-VALID-TYPE            PIC X(04).
       01  LS-VALID-DATA            PIC X(80).
       01  LS-VALID-RESULT          PIC S9(4) COMP.
       01  LS-VALID-MSG             PIC X(80).

       PROCEDURE DIVISION USING LS-VALID-TYPE
                                LS-VALID-DATA
                                LS-VALID-RESULT
                                LS-VALID-MSG.

       0000-MAIN-CONTROL.
           MOVE RC-SUCCESS TO LS-VALID-RESULT
           MOVE SPACES TO LS-VALID-MSG
           EVALUATE LS-VALID-TYPE
               WHEN 'ACCT'
                   PERFORM 1000-VALIDATE-ACCOUNT-NUM
               WHEN 'AMNT'
                   PERFORM 2000-VALIDATE-AMOUNT
               WHEN 'DATE'
                   PERFORM 3000-VALIDATE-DATE
               WHEN 'CUST'
                   PERFORM 4000-VALIDATE-CUSTOMER-NUM
               WHEN OTHER
                   MOVE RC-ERROR TO LS-VALID-RESULT
                   MOVE 'Unknown validation type.'
                       TO LS-VALID-MSG
           END-EVALUATE
           GOBACK
           .

       1000-VALIDATE-ACCOUNT-NUM.
           IF LS-VALID-DATA(1:10) = SPACES
               MOVE RC-ERROR TO LS-VALID-RESULT
               MOVE 'Account number is blank.'
                   TO LS-VALID-MSG
               GO TO 1000-EXIT
           END-IF
           MOVE LS-VALID-DATA(1:10) TO WS-NUM-TEST
           INSPECT WS-NUM-TEST
               TALLYING CTR-RECORDS-READ
               FOR ALL SPACES
           IF CTR-RECORDS-READ > 5
               MOVE RC-ERROR TO LS-VALID-RESULT
               MOVE 'Account number has excessive spaces.'
                   TO LS-VALID-MSG
           END-IF
           MOVE ZERO TO CTR-RECORDS-READ
           .
       1000-EXIT.
           EXIT.

       2000-VALIDATE-AMOUNT.
           IF LS-VALID-DATA(1:14) = SPACES
               MOVE RC-ERROR TO LS-VALID-RESULT
               MOVE 'Amount is blank.' TO LS-VALID-MSG
               GO TO 2000-EXIT
           END-IF
           IF FUNCTION NUMVAL(LS-VALID-DATA(1:14)) NOT > 0
               MOVE RC-ERROR TO LS-VALID-RESULT
               MOVE 'Amount must be greater than zero.'
                   TO LS-VALID-MSG
           END-IF
           .
       2000-EXIT.
           EXIT.

       3000-VALIDATE-DATE.
      *    Expected format: YYYY-MM-DD (10 bytes)
           IF LS-VALID-DATA(1:10) = SPACES
               MOVE RC-ERROR TO LS-VALID-RESULT
               MOVE 'Date is blank.' TO LS-VALID-MSG
               GO TO 3000-EXIT
           END-IF
           IF LS-VALID-DATA(5:1) NOT = '-'
               OR LS-VALID-DATA(8:1) NOT = '-'
               MOVE RC-ERROR TO LS-VALID-RESULT
               MOVE 'Date format must be YYYY-MM-DD.'
                   TO LS-VALID-MSG
               GO TO 3000-EXIT
           END-IF
           MOVE FUNCTION NUMVAL(LS-VALID-DATA(1:4))
               TO WS-DATE-YEAR
           MOVE FUNCTION NUMVAL(LS-VALID-DATA(6:2))
               TO WS-DATE-MONTH
           MOVE FUNCTION NUMVAL(LS-VALID-DATA(9:2))
               TO WS-DATE-DAY
           IF WS-DATE-MONTH < 1 OR WS-DATE-MONTH > 12
               MOVE RC-ERROR TO LS-VALID-RESULT
               MOVE 'Invalid month in date.'
                   TO LS-VALID-MSG
               GO TO 3000-EXIT
           END-IF
      *    Determine max days for the month
           MOVE WS-DAYS-IN-MONTH(WS-DATE-MONTH)
               TO WS-MAX-DAY
           IF WS-DATE-MONTH = 2
               PERFORM 3100-CHECK-LEAP-YEAR
               IF IS-LEAP-YEAR
                   MOVE 29 TO WS-MAX-DAY
               END-IF
           END-IF
           IF WS-DATE-DAY < 1 OR WS-DATE-DAY > WS-MAX-DAY
               MOVE RC-ERROR TO LS-VALID-RESULT
               STRING 'Invalid day for month '
                   WS-DATE-MONTH '.'
                   DELIMITED BY SIZE
                   INTO LS-VALID-MSG
           END-IF
           .
       3000-EXIT.
           EXIT.

       3100-CHECK-LEAP-YEAR.
           MOVE 'N' TO WS-LEAP-YEAR-FLAG
           IF FUNCTION MOD(WS-DATE-YEAR 4) = 0
               IF FUNCTION MOD(WS-DATE-YEAR 100) NOT = 0
                   SET IS-LEAP-YEAR TO TRUE
               ELSE
                   IF FUNCTION MOD(WS-DATE-YEAR 400) = 0
                       SET IS-LEAP-YEAR TO TRUE
                   END-IF
               END-IF
           END-IF
           .

       4000-VALIDATE-CUSTOMER-NUM.
           IF LS-VALID-DATA(1:10) = SPACES
               MOVE RC-ERROR TO LS-VALID-RESULT
               MOVE 'Customer number is blank.'
                   TO LS-VALID-MSG
           END-IF
           .

The validation subprogram uses a type-dispatch pattern: the caller passes a four-character validation type code (ACCT, AMNT, DATE, or CUST) along with the data to validate, and the program routes to the appropriate paragraph using EVALUATE. This is cleaner than having four separate validation subprograms and avoids the overhead of loading and unloading multiple modules.

The date validation in paragraph 3000-VALIDATE-DATE demonstrates several techniques working together: reference modification (Chapter 9) extracts the year, month, and day components from the formatted date string; the NUMVAL intrinsic function (Chapter 19) converts the character representations to numeric values; the days-in-month table from the constants copybook (Chapter 10) provides the maximum valid day for each month; and the leap year calculation uses the MOD intrinsic function to implement the standard algorithm.

Any program in the system can call CAPVALID before processing user input:

       CALL PGM-VALIDATION
           USING 'DATE'
                 WS-INPUT-DATE
                 WS-VALID-RC
                 WS-VALID-MSG
       END-CALL
       IF WS-VALID-RC NOT = RC-SUCCESS
           DISPLAY 'Validation failed: ' WS-VALID-MSG
       END-IF

41.11 Putting It All Together: Integration and Testing

The Community Bank application follows a standard mainframe build process. Each COBOL program is compiled separately, producing an object module. The link-editor then combines the main program (CAPMAIN) with the subprograms it calls (CAPAMGMT, CAPTRANS, CAPINQRY, CAPVALID, CAPERROR) into a single load module for interactive execution. The batch programs (CAPINTRN, CAPSTMNT, CAPRPORT) are link-edited as separate load modules, since they run independently under JCL.

The compilation order matters because of copybook dependencies. If you change a field in capstone-account.cpy, every program that copies that copybook must be recompiled. A typical build sequence is:

  1. Verify all copybooks compile cleanly (syntax check only).
  2. Compile the utility subprograms first: CAPVALID, CAPERROR.
  3. Compile the subsystem programs: CAPAMGMT, CAPTRANS, CAPINQRY.
  4. Compile the batch programs: CAPINTRN, CAPSTMNT, CAPRPORT.
  5. Compile the main menu: CAPMAIN.
  6. Link-edit the interactive load module.
  7. Link-edit each batch load module.

In a production environment, this process would be automated by a build tool or CI/CD pipeline (Chapter 40).

Test Data Strategy

Testing the Community Bank application requires a comprehensive set of test data that exercises all code paths. The test data set should include:

Customer test data: - At least 10 customer records covering all customer types (individual, business, trust) and all statuses (active, inactive, deceased, closed). - Customers with varying numbers of accounts (0, 1, 5, maximum). - Customers with addresses that test formatting edge cases (long names, blank address line 2, various state codes).

Account test data: - Accounts of every type (checking, savings, money market, CD, loan). - Accounts in every status (active, closed, frozen, dormant, pending). - Checking accounts with balances above and below the minimum balance threshold. - A CD account with a maturity date in the past (to test maturity processing). - An account with the overdraft opt-out flag set.

Transaction test data: - Deposits of varying amounts including exactly $10,000 and $10,000.01 (to test the CTR threshold). - A withdrawal that exceeds the daily maximum ($10,000 limit from the constants copybook). - A withdrawal that would overdraw the account (to test overdraft logic). - A transfer where the target account does not exist (to test error handling). - Transactions from every channel (branch, ATM, online, mobile, ACH, wire, batch).

Testing Sequence

The testing strategy follows the testing pyramid from Chapter 40:

Phase 1 -- Unit testing. Test each subprogram in isolation using a simple test driver that populates the communication area with test values and calls the subprogram. Verify that the return code and any output data match expectations. For example, a unit test for CAPTRANS would:

  1. Set up a test account with a known balance of $1,000.00.
  2. Call CAPTRANS with function code 'DEPT' and amount $500.00.
  3. Verify the account balance is now $1,500.00.
  4. Verify a transaction history record was written.
  5. Verify the return code is RC-SUCCESS.

Then test the error paths:

  1. Call CAPTRANS with an amount of -$100.00 (negative deposit).
  2. Verify the return code is RC-ERROR.
  3. Verify the error message says "Deposit amount must be positive."
  4. Verify the account balance has not changed.

Phase 2 -- Integration testing. Test the interaction between programs. For example, open an account through CAPAMGMT, process a deposit through CAPTRANS, then verify the balance through CAPINQRY. Run the interest calculation through CAPINTRN and verify that the accrued interest field is updated correctly.

Phase 3 -- Batch cycle testing. Create a full day's transaction file and run the complete JCL batch cycle. Verify that interest calculations are correct for each account type, that statements are generated for the correct accounts, and that the daily summary report balances to the input file. Verify that the CTR report captures every transaction at or above $10,000.

Phase 4 -- Regression testing. After fixing any defects found in earlier phases, re-run all tests to ensure that fixes did not introduce new problems. Maintain a library of test cases and their expected results so that regression testing can be repeated quickly after any code change.


41.12 Cross-Reference Guide: Skills Applied in This Project

One of the goals of this capstone project is to demonstrate that the skills you learned in isolation throughout this textbook work together as a coherent toolkit. The following table maps every major COBOL concept to the specific place in the Community Bank application where it is used:

Chapter Concept Application in Community Bank
Ch 2: Program Structure Four divisions, paragraph organization Every program follows the standard division structure with named paragraphs
Ch 3: Data Types & PICTURE PIC X, PIC 9, PIC S9 COMP-3, PICTURE editing All copybooks; all display formatting
Ch 4: WORKING-STORAGE Record layouts, value clauses, level structures Constants copybook, all working variables
Ch 5: Basic I/O DISPLAY and ACCEPT statements CAPMAIN menu interaction
Ch 6: Arithmetic ADD, SUBTRACT, COMPUTE, ROUNDED CAPTRANS balance updates, CAPINTRN interest calculation
Ch 7: Conditional Logic IF/EVALUATE, 88-levels, condition names Transaction validation, menu dispatch, status resolution
Ch 8: PERFORM PERFORM UNTIL, inline PERFORM, paragraph control flow Main processing loops in every program
Ch 9: String Handling STRING, INSPECT, reference modification Name formatting, date formatting, error messages
Ch 10: Tables & Arrays OCCURS, table lookup, REDEFINES Interest rate table, account type table, transaction type table, days-in-month table
Ch 11: Sequential Files Sequential READ/WRITE, file status Transaction input file, all report output files
Ch 12: Indexed Files VSAM KSDS, READ/WRITE/REWRITE, random and dynamic access Customer, account, and transaction history files
Ch 14: SORT/MERGE SORT with INPUT/OUTPUT PROCEDURE, RELEASE/RETURN CAPRPORT transaction sorting
Ch 15: Report Writer Page headers, footers, detail lines, page control CAPINTRN and CAPRPORT report generation
Ch 16: Declaratives & Exceptions File status checking after every I/O All programs, every file operation
Ch 17: Subprograms & CALL CALL USING, LINKAGE SECTION, GOBACK CAPMAIN calling all subsystem programs
Ch 18: Copybooks COPY statement, shared data layouts All four .CPY files used across all programs
Ch 19: Intrinsic Functions CURRENT-DATE, NUMVAL, MOD, FUNCTION ALL INTRINSIC Date handling, validation, interest calculation
Ch 21: Coding Standards Naming conventions, commenting, structure Consistent naming throughout all programs
Ch 27: JCL Job streams, COND parameter, DD statements Nightly batch processing JCL
Ch 33: Financial Calculations Interest computation, decimal precision, rounding CAPINTRN daily interest accrual
Ch 34: Banking Systems Core banking concepts, CTR reporting, account types Overall system design and regulatory reporting
Ch 40: Testing & QA Test data strategy, testing pyramid, regression Testing sequence described in Section 41.11

41.13 Design Patterns Worth Noting

Several design patterns appear throughout the Community Bank application that are worth calling out explicitly, because they represent the accumulated wisdom of decades of mainframe COBOL development.

The Communication Area Pattern

Every call from CAPMAIN to a subsystem program passes a standardized communication area (WS-COMM-AREA). This pattern, which is the COBOL equivalent of a function parameter object, provides several benefits:

  • Uniform interface. Every subsystem program has the same first parameter, making the calling convention predictable and easy to maintain.
  • Extensibility. New fields can be added to the communication area without changing the CALL statements in the main program, as long as the new fields are appended at the end.
  • Traceability. The communication area carries the user ID, timestamp, and function code, creating an implicit audit trail for every operation.

This pattern is used universally in CICS applications (Chapter 24-25), where the COMMAREA is the standard mechanism for passing data between transactions.

The Initialize-Process-Terminate Pattern

Every program in the system follows the same top-level structure:

       0000-MAIN-CONTROL.
           PERFORM 1000-INITIALIZE
           PERFORM 2000-PROCESS-xxxx
               UNTIL END-CONDITION
           PERFORM 9000-TERMINATE
           STOP RUN (or GOBACK)
           .

This pattern, sometimes called the "IPT pattern" or the "mainline pattern," ensures that file opens happen before processing, file closes happen after processing, and the main processing loop has clear entry and exit points. The 1000/2000/9000 numbering convention leaves room for additional major sections without renumbering.

The Status-Check-After-Every-I/O Pattern

Without exception, every file I/O operation in the Community Bank application is followed by a check of the file status variable:

           READ ACCT-FILE INTO ACCOUNT-RECORD
               KEY IS ACCT-NUMBER
           IF WS-ACCT-STATUS NOT = '00'
               MOVE RC-ERROR TO LS-RETURN-CODE
               MOVE 'Account not found.' TO LS-ERROR-MSG
               GO TO exit-paragraph
           END-IF

This is not a matter of style preference. In production banking systems, unchecked I/O errors are treated as defects of the highest severity. A READ that silently returns stale data because the file status was not checked can lead to a cascade of incorrect balance postings that takes weeks to unwind. Chapter 16 (Declaratives and File Exception Handling) covered the technical mechanisms; this project demonstrates the discipline of applying them consistently.

The Utility Subprogram Pattern

The CAPVALID and CAPERROR programs are utility subprograms that provide shared services to all other programs. This pattern reduces code duplication (every program would otherwise have its own date validation routine, its own error formatting logic, etc.) and ensures consistency (every program validates dates the same way).

In larger banking systems, the utility subprogram library may contain dozens of modules: date arithmetic, check digit calculation, account number formatting, currency conversion, encryption wrappers, logging, and more. The Community Bank application demonstrates the pattern with two modules; the same architecture scales to hundreds.


41.14 Extending the Project

The Community Bank application as presented in this chapter is a complete, working system, but it is deliberately designed to be extended. Here are several enhancements that will deepen your skills and prepare you for the kind of work you will do in a professional COBOL development role.

Enhancement 1: Add DB2 Integration

Replace the VSAM files with DB2 tables. Rewrite the file I/O operations as embedded SQL (Chapter 22-23). This enhancement teaches you:

  • Creating DB2 tables with appropriate column types, primary keys, and indexes.
  • Replacing READ/WRITE/REWRITE with SELECT/INSERT/UPDATE statements.
  • Using DB2 COMMIT and ROLLBACK for transaction atomicity (ensuring the transfer operation either fully succeeds or fully fails).
  • Writing a DCLGEN copybook to replace the VSAM record layout copybooks.

Enhancement 2: Add a CICS Online Interface

Replace the DISPLAY/ACCEPT interface in CAPMAIN with a CICS BMS screen (Chapter 24-25). This enhancement teaches you:

  • Designing BMS maps for the main menu, transaction entry, and inquiry screens.
  • Using EXEC CICS SEND MAP and RECEIVE MAP for screen I/O.
  • Managing pseudo-conversational programming with COMMAREA.
  • Handling CICS abends with HANDLE ABEND and HANDLE CONDITION.

Enhancement 3: Add Loan Payment Processing

Extend the account types to include loan accounts with payment schedules. This teaches you:

  • Amortization calculation (Chapter 33) to determine principal and interest portions of each payment.
  • Date arithmetic to track payment due dates and calculate late fees.
  • A new batch program to generate payment-due notices and process automatic payments.

Enhancement 4: Add ACH File Processing

Build a program that reads an ACH file in NACHA format and posts the transactions. This teaches you:

  • Fixed-format record parsing with multiple record types in a single file.
  • Batch/detail control totals and hash totals for file validation.
  • The real-world ACH processing workflow that handles millions of payroll, bill payment, and direct deposit transactions daily.

Enhancement 5: Add Automated Testing with COBOL-Check

Write unit tests for every paragraph in CAPTRANS and CAPVALID using the COBOL-Check framework (Chapter 40). This teaches you:

  • Writing test cases that verify both success and failure paths.
  • Using mocks to isolate file I/O from business logic.
  • Building a regression test suite that runs automatically after every code change.

Enhancement 6: Add XML/JSON Web Service Integration

Build a COBOL program that exposes account inquiry as a JSON web service (Chapter 38). This teaches you:

  • XML GENERATE and JSON GENERATE statements.
  • Interfacing COBOL programs with web service frameworks.
  • The modern integration patterns that allow mainframe COBOL to participate in API-driven architectures.

41.15 What You Have Learned

This capstone project has brought together the complete arc of your COBOL education. Let us review what you have accomplished and the skills you have demonstrated.

System design and architecture. You designed a multi-program banking application with a clear separation of concerns: a menu dispatcher, subsystem programs for each business function, utility subprograms for shared services, and batch programs for overnight processing. You understood how COBOL's CALL statement creates a modular architecture analogous to function calls in other languages, and how copybooks enforce data consistency across the entire system.

VSAM file processing. You implemented customer, account, and transaction files as VSAM Key-Sequenced Data Sets, using random access for single-record operations and dynamic access for combined random-and-sequential processing. You used primary keys for direct lookup and alternate keys for relationship-based browsing (finding all accounts for a given customer).

Financial calculations. You implemented daily interest accrual using the simple daily rate method with correct leap year handling, COMPUTE ROUNDED for banker's rounding, and three-tier interest accumulation (accrued, month-to-date, year-to-date). You understood why COMP-3 packed decimal is the only acceptable storage format for monetary values on the mainframe.

Transaction processing and business rules. You built a transaction processing engine that validates every operation against business rules (positive amounts, sufficient funds, active account status, daily limits, overdraft opt-out preferences) before modifying any data. You understood the importance of recording balance-before and balance-after for auditability.

Batch processing and JCL. You wrote a JCL job stream that orchestrates the nightly batch cycle with conditional step execution, ensuring that downstream programs do not run if upstream programs fail. You used the SORT verb with INPUT and OUTPUT PROCEDURES to order transactions for reporting while simultaneously checking for CTR-reportable amounts.

Report generation. You produced formatted reports with page headers, column headers, detail lines, page breaks, and summary totals. You generated both operational reports (daily transaction summary) and regulatory reports (Currency Transaction Report).

Input validation and error handling. You built a centralized validation subprogram that verifies account numbers, amounts, dates, and customer numbers using techniques including reference modification, intrinsic functions, table lookups, and the leap year algorithm. You checked file status after every I/O operation and handled ON EXCEPTION for every CALL.

Coding standards and maintainability. You followed consistent naming conventions (paragraph numbering, field prefixes, copybook organization), documented every program with a header comment block, and placed all constants and magic numbers in a shared copybook where they can be changed in one place.

Testing strategy. You designed a multi-phase testing approach progressing from unit testing of individual subprograms through integration testing of program interactions to full batch cycle testing with comprehensive test data that exercises every code path and edge case.

This is not a classroom exercise that you will never see again. The architecture, patterns, and techniques in the Community Bank application are the same ones used in the COBOL systems that process trillions of dollars every day. The programs you have written in this chapter are structurally identical to programs running in production at banks, insurance companies, and government agencies right now.

You began this textbook by learning what COBOL is and why it matters. You end it by building the kind of system that makes it matter. The skills you have developed are in high demand and short supply. The global economy runs on COBOL, and you now have the knowledge to keep it running -- and to make it better.

Welcome to the profession.