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...
In This Chapter
- Part IX - Capstone Projects and Career Path
- 41.1 Project Overview and Requirements Analysis
- 41.2 System Architecture
- 41.3 The Data Design
- 41.4 Building the Main Menu Program (CAPMAIN)
- 41.5 Building the Transaction Processing Subsystem (CAPTRANS)
- 41.6 Building the Account Inquiry Program (CAPINQRY)
- 41.9 JCL for the Batch Processing Cycle
- 41.10 The Validation Subprogram (CAPVALID)
- 41.11 Putting It All Together: Integration and Testing
- 41.12 Cross-Reference Guide: Skills Applied in This Project
- 41.13 Design Patterns Worth Noting
- 41.14 Extending the Project
- 41.15 What You Have Learned
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:
- Customer Management: Add new customers, update customer information, inquire on customer records, and deactivate customers who close all accounts.
- Account Management: Open new accounts, close accounts, modify account attributes, and perform account inquiries that display balances and recent activity.
- 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.
- 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.
- 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 $$$,$$$, MATH9 MATH10 $,$$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
Compilation and Link Strategy
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:
- Verify all copybooks compile cleanly (syntax check only).
- Compile the utility subprograms first: CAPVALID, CAPERROR.
- Compile the subsystem programs: CAPAMGMT, CAPTRANS, CAPINQRY.
- Compile the batch programs: CAPINTRN, CAPSTMNT, CAPRPORT.
- Compile the main menu: CAPMAIN.
- Link-edit the interactive load module.
- 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:
- Set up a test account with a known balance of $1,000.00.
- Call CAPTRANS with function code 'DEPT' and amount $500.00.
- Verify the account balance is now $1,500.00.
- Verify a transaction history record was written.
- Verify the return code is RC-SUCCESS.
Then test the error paths:
- Call CAPTRANS with an amount of -$100.00 (negative deposit).
- Verify the return code is RC-ERROR.
- Verify the error message says "Deposit amount must be positive."
- 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.