Case Study 1: Building an Account Inquiry Transaction
Background
Heartland Community Bank operates 42 branches across the Midwest. Every branch teller needs fast access to customer account information -- balance, account status, recent holds, and last activity date. In the legacy environment, tellers had to call the back office, which ran a batch report. Response times were measured in minutes.
The bank's IT department was tasked with building an online account inquiry system using CICS on their IBM z/OS mainframe. The system would give every teller instant access to account data through a 3270 terminal screen. The transaction needed to be pseudo-conversational to support the bank's 350 concurrent teller terminals without exhausting CICS resources.
This case study walks through the complete design and implementation of the AINQ (Account Inquiry) transaction -- from BMS map definition to COBOL program logic to COMMAREA design.
Project Requirements
Functional Requirements
- The teller types transaction ID "AINQ" and presses Enter to start the inquiry
- The system displays an empty inquiry screen with the cursor on the account number field
- The teller enters a 10-digit account number and presses Enter
- The system reads the account from a VSAM KSDS file and displays: - Account number and type (checking, savings, money market) - Customer name and membership date - Current balance, available balance, and hold amount - Account status (active, closed, frozen, dormant) - Last activity date and teller ID
- The teller can enter a new account number to inquire on another account
- PF3 exits the transaction
- PF5 refreshes the current account display
- Error messages appear on line 23 in red
Non-Functional Requirements
- Response time under 500 milliseconds from Enter key to screen display
- Support 350 concurrent terminals
- COMMAREA must not exceed 500 bytes
- All currency amounts formatted with dollar signs and commas
- Account numbers validated before file access (10 numeric digits required)
BMS Map Definition
The BMS map defines the screen layout -- the field positions, attributes, labels, and input/output areas.
The Mapset and Map Macros
***********************************************************************
* AINQMS - ACCOUNT INQUIRY MAP SET *
* CONTAINS: AINQMAP - ACCOUNT INQUIRY SCREEN *
***********************************************************************
AINQMS DFHMSD TYPE=&SYSPARM, X
MODE=INOUT, X
LANG=COBOL, X
TIOAPFX=YES, X
STORAGE=AUTO, X
CTRL=(FREEKB,FRSET)
***********************************************************************
AINQMAP DFHMDI SIZE=(24,80), X
LINE=1, X
COLUMN=1
***********************************************************************
* ROW 1: TITLE LINE *
***********************************************************************
DFHMDF POS=(01,01), X
LENGTH=20, X
ATTRB=(ASKIP,BRT), X
INITIAL='HEARTLAND COMMUNITY'
DFHMDF POS=(01,25), X
LENGTH=30, X
ATTRB=(ASKIP,BRT), X
INITIAL='A C C O U N T I N Q U I R Y'
DATEFLD DFHMDF POS=(01,65), X
LENGTH=10, X
ATTRB=(ASKIP,NORM)
***********************************************************************
* ROW 2: SEPARATOR *
***********************************************************************
DFHMDF POS=(02,01), X
LENGTH=78, X
ATTRB=(ASKIP,NORM), X
INITIAL='---------------------------------------------X
---------------------------------'
***********************************************************************
* ROW 4: ACCOUNT NUMBER INPUT *
***********************************************************************
DFHMDF POS=(04,02), X
LENGTH=16, X
ATTRB=(ASKIP,NORM), X
INITIAL='Account Number:'
ACCTNO DFHMDF POS=(04,19), X
LENGTH=10, X
ATTRB=(UNPROT,BRT,IC), X
PICIN='X(10)', X
PICOUT='X(10)'
DFHMDF POS=(04,30), X
LENGTH=01, X
ATTRB=ASKIP
***********************************************************************
* ROWS 6-8: CUSTOMER INFORMATION *
***********************************************************************
DFHMDF POS=(06,02), X
LENGTH=14, X
ATTRB=(ASKIP,NORM), X
INITIAL='Customer Name:'
CNME DFHMDF POS=(06,19), X
LENGTH=30, X
ATTRB=(ASKIP,BRT)
*
DFHMDF POS=(07,02), X
LENGTH=14, X
ATTRB=(ASKIP,NORM), X
INITIAL='Account Type: '
ATYP DFHMDF POS=(07,19), X
LENGTH=15, X
ATTRB=(ASKIP,NORM)
*
DFHMDF POS=(08,02), X
LENGTH=14, X
ATTRB=(ASKIP,NORM), X
INITIAL='Member Since: '
MDATE DFHMDF POS=(08,19), X
LENGTH=10, X
ATTRB=(ASKIP,NORM)
***********************************************************************
* ROWS 10-14: BALANCE INFORMATION *
***********************************************************************
DFHMDF POS=(10,02), X
LENGTH=40, X
ATTRB=(ASKIP,NORM), X
INITIAL='---------- Balance Information ----------'
*
DFHMDF POS=(11,02), X
LENGTH=18, X
ATTRB=(ASKIP,NORM), X
INITIAL='Current Balance: '
CURBAL DFHMDF POS=(11,22), X
LENGTH=15, X
ATTRB=(ASKIP,BRT), X
PICOUT='$$,$$$,$$9.99'
*
DFHMDF POS=(12,02), X
LENGTH=18, X
ATTRB=(ASKIP,NORM), X
INITIAL='Hold Amount: '
HLDAMT DFHMDF POS=(12,22), X
LENGTH=15, X
ATTRB=(ASKIP,NORM), X
PICOUT='$$,$$$,$$9.99'
*
DFHMDF POS=(13,02), X
LENGTH=18, X
ATTRB=(ASKIP,NORM), X
INITIAL='Available Balance:'
AVLBAL DFHMDF POS=(13,22), X
LENGTH=15, X
ATTRB=(ASKIP,BRT), X
PICOUT='$$,$$$,$$9.99'
***********************************************************************
* ROWS 15-17: STATUS INFORMATION *
***********************************************************************
DFHMDF POS=(15,02), X
LENGTH=40, X
ATTRB=(ASKIP,NORM), X
INITIAL='---------- Status Information -----------'
*
DFHMDF POS=(16,02), X
LENGTH=18, X
ATTRB=(ASKIP,NORM), X
INITIAL='Account Status: '
ACSTAT DFHMDF POS=(16,22), X
LENGTH=10, X
ATTRB=(ASKIP,NORM)
*
DFHMDF POS=(17,02), X
LENGTH=18, X
ATTRB=(ASKIP,NORM), X
INITIAL='Last Activity: '
LSTACT DFHMDF POS=(17,22), X
LENGTH=10, X
ATTRB=(ASKIP,NORM)
*
DFHMDF POS=(18,02), X
LENGTH=18, X
ATTRB=(ASKIP,NORM), X
INITIAL='Last Teller: '
LSTTEL DFHMDF POS=(18,22), X
LENGTH=08, X
ATTRB=(ASKIP,NORM)
***********************************************************************
* ROW 22: FUNCTION KEY LEGEND *
***********************************************************************
DFHMDF POS=(22,02), X
LENGTH=60, X
ATTRB=(ASKIP,NORM), X
INITIAL='PF3=Exit PF5=Refresh Enter=InquirX
y'
***********************************************************************
* ROW 23: ERROR/INFO MESSAGE LINE *
***********************************************************************
ERRMSG DFHMDF POS=(23,02), X
LENGTH=70, X
ATTRB=(ASKIP,BRT), X
COLOR=RED
***********************************************************************
DFHMSD TYPE=FINAL
END
The Generated Symbolic Map
When the BMS macros are assembled with TYPE=DSECT, the COBOL copybook is generated. The copybook contains both an input structure and an output structure:
* GENERATED BY BMS - DO NOT MODIFY
* MAPSET: AINQMS MAP: AINQMAP
01 AINQMAPI.
02 FILLER PIC X(12).
* DATE FIELD
02 DATEFLDL PIC S9(04) COMP.
02 DATEFLDF PIC X(01).
02 FILLER REDEFINES DATEFLDF.
03 DATEFLDA PIC X(01).
02 DATEFLDI PIC X(10).
* ACCOUNT NUMBER
02 ACCTNOL PIC S9(04) COMP.
02 ACCTNOF PIC X(01).
02 FILLER REDEFINES ACCTNOF.
03 ACCTNOA PIC X(01).
02 ACCTNOI PIC X(10).
* CUSTOMER NAME
02 CNMEL PIC S9(04) COMP.
02 CNMEF PIC X(01).
02 FILLER REDEFINES CNMEF.
03 CNMEA PIC X(01).
02 CNMEI PIC X(30).
* (additional fields follow same pattern)
02 ATYPL PIC S9(04) COMP.
02 ATYPF PIC X(01).
02 FILLER REDEFINES ATYPF.
03 ATYPA PIC X(01).
02 ATYPI PIC X(15).
02 MDATEL PIC S9(04) COMP.
02 MDATEF PIC X(01).
02 FILLER REDEFINES MDATEF.
03 MDATEA PIC X(01).
02 MDATEI PIC X(10).
02 CURBALL PIC S9(04) COMP.
02 CURBALF PIC X(01).
02 FILLER REDEFINES CURBALF.
03 CURBALA PIC X(01).
02 CURBALI PIC X(15).
02 HLDAMTL PIC S9(04) COMP.
02 HLDAMTF PIC X(01).
02 FILLER REDEFINES HLDAMTF.
03 HLDAMTA PIC X(01).
02 HLDAMTI PIC X(15).
02 AVLBALL PIC S9(04) COMP.
02 AVLBALF PIC X(01).
02 FILLER REDEFINES AVLBALF.
03 AVLBALA PIC X(01).
02 AVLBALI PIC X(15).
02 ACSTATL PIC S9(04) COMP.
02 ACSTATF PIC X(01).
02 FILLER REDEFINES ACSTATF.
03 ACSTATA PIC X(01).
02 ACSTATI PIC X(10).
02 LSTACTL PIC S9(04) COMP.
02 LSTACTF PIC X(01).
02 FILLER REDEFINES LSTACTF.
03 LSTACTA PIC X(01).
02 LSTACTI PIC X(10).
02 LSTTELL PIC S9(04) COMP.
02 LSTTELF PIC X(01).
02 FILLER REDEFINES LSTTELF.
03 LSTTELA PIC X(01).
02 LSTTELI PIC X(08).
02 ERRMSGL PIC S9(04) COMP.
02 ERRMSGF PIC X(01).
02 FILLER REDEFINES ERRMSGF.
03 ERRMSGA PIC X(01).
02 ERRMSGI PIC X(70).
01 AINQMAPO REDEFINES AINQMAPI.
02 FILLER PIC X(12).
02 FILLER PIC X(03).
02 DATEFLDO PIC X(10).
02 FILLER PIC X(03).
02 ACCTNOO PIC X(10).
02 FILLER PIC X(03).
02 CNMEO PIC X(30).
02 FILLER PIC X(03).
02 ATYPO PIC X(15).
02 FILLER PIC X(03).
02 MDATEO PIC X(10).
02 FILLER PIC X(03).
02 CURBALO PIC 9(11)V99.
02 FILLER PIC X(03).
02 HLDAMTO PIC 9(11)V99.
02 FILLER PIC X(03).
02 AVLBALO PIC 9(11)V99.
02 FILLER PIC X(03).
02 ACSTATO PIC X(10).
02 FILLER PIC X(03).
02 LSTACTO PIC X(10).
02 FILLER PIC X(03).
02 LSTTELO PIC X(08).
02 FILLER PIC X(03).
02 ERRMSGO PIC X(70).
COMMAREA Design
The COMMAREA preserves state between pseudo-conversational invocations. For the account inquiry, the COMMAREA is modest -- it holds the current state and the last-viewed account number:
*---------------------------------------------------------
* COMMAREA COPYBOOK: AINQCOMM
*---------------------------------------------------------
01 WS-COMM-AREA.
05 CA-STATE PIC X(01).
88 CA-FIRST-TIME VALUE 'F'.
88 CA-INQUIRY VALUE 'I'.
88 CA-DISPLAY VALUE 'D'.
05 CA-LAST-ACCT-NO PIC X(10).
05 CA-LAST-ACCT-NAME PIC X(30).
05 CA-ERROR-FLAG PIC X(01).
88 CA-NO-ERROR VALUE 'N'.
88 CA-HAS-ERROR VALUE 'Y'.
05 CA-ERROR-MSG PIC X(70).
05 FILLER PIC X(88).
* Total COMMAREA: 200 bytes
The COMMAREA is intentionally small. It stores only the navigation state and the last account number (for PF5 refresh). The full account record is NOT stored in the COMMAREA -- it is re-read from the VSAM file on each display. This keeps the COMMAREA under 200 bytes and ensures the displayed data is always current.
COBOL Program Implementation
Complete Program Source
IDENTIFICATION DIVISION.
PROGRAM-ID. AINQPROG.
*---------------------------------------------------------
* PROGRAM: AINQPROG
* TRANS: AINQ
* PURPOSE: Account Inquiry - Pseudo-conversational
* AUTHOR: Heartland IT - Online Banking Team
*---------------------------------------------------------
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
*---------------------------------------------------------
* Standard CICS copybooks
*---------------------------------------------------------
COPY DFHAID.
COPY DFHBMSCA.
*---------------------------------------------------------
* BMS symbolic map
*---------------------------------------------------------
COPY AINQMS.
*---------------------------------------------------------
* COMMAREA work area
*---------------------------------------------------------
01 WS-COMM-AREA.
05 CA-STATE PIC X(01).
88 CA-FIRST-TIME VALUE 'F'.
88 CA-INQUIRY VALUE 'I'.
88 CA-DISPLAY VALUE 'D'.
05 CA-LAST-ACCT-NO PIC X(10).
05 CA-LAST-ACCT-NAME PIC X(30).
05 CA-ERROR-FLAG PIC X(01).
88 CA-NO-ERROR VALUE 'N'.
88 CA-HAS-ERROR VALUE 'Y'.
05 CA-ERROR-MSG PIC X(70).
05 FILLER PIC X(88).
*---------------------------------------------------------
* VSAM account record layout
*---------------------------------------------------------
01 WS-ACCT-RECORD.
05 WS-ACCT-KEY PIC X(10).
05 WS-ACCT-TYPE PIC X(02).
05 WS-CUST-NAME PIC X(30).
05 WS-MEMBER-DATE PIC X(10).
05 WS-CURRENT-BAL PIC S9(11)V99 COMP-3.
05 WS-HOLD-AMOUNT PIC S9(11)V99 COMP-3.
05 WS-ACCT-STATUS PIC X(01).
05 WS-LAST-ACTIVITY PIC X(10).
05 WS-LAST-TELLER PIC X(08).
05 FILLER PIC X(50).
01 WS-ACCT-REC-LEN PIC S9(04) COMP
VALUE 132.
*---------------------------------------------------------
* Work fields
*---------------------------------------------------------
01 WS-RESP PIC S9(08) COMP.
01 WS-RESP2 PIC S9(08) COMP.
01 WS-AVAILABLE-BAL PIC S9(11)V99 COMP-3.
01 WS-ACCT-TYPE-DESC PIC X(15).
01 WS-CURRENT-DATE-DATA.
05 WS-DATE-YYYY PIC 9(04).
05 WS-DATE-MM PIC 9(02).
05 WS-DATE-DD PIC 9(02).
05 FILLER PIC X(13).
01 WS-DISPLAY-DATE PIC X(10).
01 WS-INPUT-ACCT PIC X(10).
01 WS-VALID-INPUT PIC X(01) VALUE 'Y'.
88 INPUT-IS-VALID VALUE 'Y'.
88 INPUT-IS-INVALID VALUE 'N'.
01 WS-COMM-LENGTH PIC S9(04) COMP
VALUE 200.
LINKAGE SECTION.
01 DFHCOMMAREA PIC X(200).
PROCEDURE DIVISION.
*---------------------------------------------------------
* 0000-MAIN: Determine processing based on state
*---------------------------------------------------------
0000-MAIN.
EVALUATE TRUE
WHEN EIBCALEN = 0
PERFORM 1000-FIRST-TIME
WHEN OTHER
MOVE DFHCOMMAREA TO WS-COMM-AREA
PERFORM 2000-PROCESS-INPUT
END-EVALUATE
EXEC CICS RETURN TRANSID('AINQ')
COMMAREA(WS-COMM-AREA)
LENGTH(WS-COMM-LENGTH)
END-EXEC
.
*---------------------------------------------------------
* 1000-FIRST-TIME: Initial display of empty inquiry screen
*---------------------------------------------------------
1000-FIRST-TIME.
INITIALIZE WS-COMM-AREA
SET CA-INQUIRY TO TRUE
SET CA-NO-ERROR TO TRUE
PERFORM 7000-GET-DATE
PERFORM 8000-SEND-EMPTY-MAP
.
*---------------------------------------------------------
* 2000-PROCESS-INPUT: Handle user input based on AID key
*---------------------------------------------------------
2000-PROCESS-INPUT.
EVALUATE EIBAID
WHEN DFHPF3
EXEC CICS SEND TEXT
FROM(WS-GOODBYE-MSG)
LENGTH(30)
ERASE
FREEKB
END-EXEC
EXEC CICS RETURN END-EXEC
WHEN DFHPF5
IF CA-LAST-ACCT-NO NOT = SPACES
MOVE CA-LAST-ACCT-NO
TO WS-INPUT-ACCT
PERFORM 3000-INQUIRY-ACCOUNT
ELSE
MOVE 'No account to refresh'
TO CA-ERROR-MSG
SET CA-HAS-ERROR TO TRUE
PERFORM 8000-SEND-EMPTY-MAP
END-IF
WHEN DFHENTER
PERFORM 2100-RECEIVE-MAP
IF INPUT-IS-VALID
PERFORM 3000-INQUIRY-ACCOUNT
ELSE
PERFORM 8100-SEND-ERROR-MAP
END-IF
WHEN DFHCLEAR
PERFORM 1000-FIRST-TIME
WHEN DFHPA1
CONTINUE
WHEN DFHPA2
CONTINUE
WHEN OTHER
MOVE 'Invalid key. Use Enter, PF3,'
TO CA-ERROR-MSG
STRING CA-ERROR-MSG DELIMITED BY ' '
' PF5, or Clear'
DELIMITED BY SIZE
INTO CA-ERROR-MSG
END-STRING
SET CA-HAS-ERROR TO TRUE
PERFORM 8100-SEND-ERROR-MAP
END-EVALUATE
.
01 WS-GOODBYE-MSG PIC X(30)
VALUE 'Thank you. Session ended. '.
*---------------------------------------------------------
* 2100-RECEIVE-MAP: Get user input from the screen
*---------------------------------------------------------
2100-RECEIVE-MAP.
EXEC CICS RECEIVE MAP('AINQMAP')
MAPSET('AINQMS')
INTO(AINQMAPI)
RESP(WS-RESP)
END-EXEC
EVALUATE WS-RESP
WHEN DFHRESP(NORMAL)
MOVE ACCTNOI TO WS-INPUT-ACCT
PERFORM 2200-VALIDATE-ACCT-NO
WHEN DFHRESP(MAPFAIL)
MOVE 'Please enter an account number'
TO CA-ERROR-MSG
SET CA-HAS-ERROR TO TRUE
SET INPUT-IS-INVALID TO TRUE
WHEN OTHER
MOVE 'Screen input error occurred'
TO CA-ERROR-MSG
SET CA-HAS-ERROR TO TRUE
SET INPUT-IS-INVALID TO TRUE
END-EVALUATE
.
*---------------------------------------------------------
* 2200-VALIDATE-ACCT-NO: Verify account number format
*---------------------------------------------------------
2200-VALIDATE-ACCT-NO.
SET INPUT-IS-VALID TO TRUE
IF WS-INPUT-ACCT = SPACES
OR WS-INPUT-ACCT = LOW-VALUES
MOVE 'Account number is required'
TO CA-ERROR-MSG
SET CA-HAS-ERROR TO TRUE
SET INPUT-IS-INVALID TO TRUE
ELSE
INSPECT WS-INPUT-ACCT
TALLYING WS-RESP
FOR ALL SPACES
IF WS-INPUT-ACCT IS NOT NUMERIC
MOVE 'Account number must be 10 digits'
TO CA-ERROR-MSG
SET CA-HAS-ERROR TO TRUE
SET INPUT-IS-INVALID TO TRUE
END-IF
END-IF
.
*---------------------------------------------------------
* 3000-INQUIRY-ACCOUNT: Read VSAM file and display data
*---------------------------------------------------------
3000-INQUIRY-ACCOUNT.
MOVE WS-INPUT-ACCT TO WS-ACCT-KEY
EXEC CICS READ FILE('ACCTFIL')
INTO(WS-ACCT-RECORD)
RIDFLD(WS-ACCT-KEY)
LENGTH(WS-ACCT-REC-LEN)
RESP(WS-RESP)
RESP2(WS-RESP2)
END-EXEC
EVALUATE WS-RESP
WHEN DFHRESP(NORMAL)
MOVE WS-ACCT-KEY TO CA-LAST-ACCT-NO
MOVE WS-CUST-NAME TO CA-LAST-ACCT-NAME
SET CA-DISPLAY TO TRUE
SET CA-NO-ERROR TO TRUE
PERFORM 4000-BUILD-DISPLAY
PERFORM 8200-SEND-DATA-MAP
WHEN DFHRESP(NOTFND)
MOVE SPACES TO CA-ERROR-MSG
STRING 'Account ' DELIMITED BY SIZE
WS-INPUT-ACCT DELIMITED BY SIZE
' not found' DELIMITED BY SIZE
INTO CA-ERROR-MSG
END-STRING
SET CA-HAS-ERROR TO TRUE
PERFORM 8100-SEND-ERROR-MAP
WHEN DFHRESP(DISABLED)
MOVE 'Account file is temporarily '
TO CA-ERROR-MSG
STRING CA-ERROR-MSG DELIMITED BY ' '
'unavailable. Try later.'
DELIMITED BY SIZE
INTO CA-ERROR-MSG
END-STRING
SET CA-HAS-ERROR TO TRUE
PERFORM 8100-SEND-ERROR-MAP
WHEN OTHER
MOVE SPACES TO CA-ERROR-MSG
STRING 'File error. RESP='
DELIMITED BY SIZE
INTO CA-ERROR-MSG
END-STRING
SET CA-HAS-ERROR TO TRUE
PERFORM 8100-SEND-ERROR-MAP
END-EVALUATE
.
*---------------------------------------------------------
* 4000-BUILD-DISPLAY: Populate output map fields
*---------------------------------------------------------
4000-BUILD-DISPLAY.
INITIALIZE AINQMAPO
PERFORM 7000-GET-DATE
MOVE WS-DISPLAY-DATE TO DATEFLDO
MOVE WS-ACCT-KEY TO ACCTNOO
MOVE WS-CUST-NAME TO CNMEO
MOVE WS-MEMBER-DATE TO MDATEO
* Decode account type
EVALUATE WS-ACCT-TYPE
WHEN 'CK'
MOVE 'Checking' TO ATYPO
WHEN 'SV'
MOVE 'Savings' TO ATYPO
WHEN 'MM'
MOVE 'Money Market' TO ATYPO
WHEN 'CD'
MOVE 'Certificate' TO ATYPO
WHEN OTHER
MOVE 'Unknown' TO ATYPO
END-EVALUATE
* Set balance fields
MOVE WS-CURRENT-BAL TO CURBALO
MOVE WS-HOLD-AMOUNT TO HLDAMTO
COMPUTE WS-AVAILABLE-BAL =
WS-CURRENT-BAL - WS-HOLD-AMOUNT
MOVE WS-AVAILABLE-BAL TO AVLBALO
* Decode account status
EVALUATE WS-ACCT-STATUS
WHEN 'A'
MOVE 'Active' TO ACSTATO
WHEN 'C'
MOVE 'Closed' TO ACSTATO
MOVE DFHBMBRY TO ACSTATA
WHEN 'F'
MOVE 'Frozen' TO ACSTATO
MOVE DFHBMBRY TO ACSTATA
WHEN 'D'
MOVE 'Dormant' TO ACSTATO
WHEN OTHER
MOVE 'Unknown' TO ACSTATO
END-EVALUATE
MOVE WS-LAST-ACTIVITY TO LSTACTO
MOVE WS-LAST-TELLER TO LSTTELO
* Clear error message on successful display
MOVE SPACES TO ERRMSGO
.
*---------------------------------------------------------
* 7000-GET-DATE: Format current date for display
*---------------------------------------------------------
7000-GET-DATE.
MOVE FUNCTION CURRENT-DATE TO
WS-CURRENT-DATE-DATA
STRING WS-DATE-MM '/' DELIMITED BY SIZE
WS-DATE-DD '/' DELIMITED BY SIZE
WS-DATE-YYYY DELIMITED BY SIZE
INTO WS-DISPLAY-DATE
END-STRING
.
*---------------------------------------------------------
* 8000-SEND-EMPTY-MAP: Send map with no data (first time)
*---------------------------------------------------------
8000-SEND-EMPTY-MAP.
INITIALIZE AINQMAPO
PERFORM 7000-GET-DATE
MOVE WS-DISPLAY-DATE TO DATEFLDO
IF CA-HAS-ERROR
MOVE CA-ERROR-MSG TO ERRMSGO
SET CA-NO-ERROR TO TRUE
END-IF
EXEC CICS SEND MAP('AINQMAP')
MAPSET('AINQMS')
FROM(AINQMAPO)
ERASE
FREEKB
CURSOR
END-EXEC
.
*---------------------------------------------------------
* 8100-SEND-ERROR-MAP: Resend with error message
*---------------------------------------------------------
8100-SEND-ERROR-MAP.
MOVE CA-ERROR-MSG TO ERRMSGO
MOVE DFHBMBRY TO ERRMSGA
EXEC CICS SEND MAP('AINQMAP')
MAPSET('AINQMS')
FROM(AINQMAPO)
DATAONLY
FREEKB
CURSOR
END-EXEC
SET CA-NO-ERROR TO TRUE
.
*---------------------------------------------------------
* 8200-SEND-DATA-MAP: Send map with account data
*---------------------------------------------------------
8200-SEND-DATA-MAP.
EXEC CICS SEND MAP('AINQMAP')
MAPSET('AINQMS')
FROM(AINQMAPO)
ERASE
FREEKB
CURSOR
END-EXEC
.
Resource Definitions
The CICS system programmer defines the transaction and program using CICS RDO (Resource Definition Online):
DEFINE TRANSACTION(AINQ)
GROUP(BANKGRP)
PROGRAM(AINQPROG)
TWASIZE(0)
PROFILE(DFHCICST)
TASKDATALOC(ANY)
TASKDATAKEY(USER)
DEFINE PROGRAM(AINQPROG)
GROUP(BANKGRP)
LANGUAGE(COBOL)
DATALOCATION(ANY)
DEFINE FILE(ACCTFIL)
GROUP(BANKGRP)
DSNAME(BANK.PROD.ACCOUNTS)
RLSACCESS(NO)
STATUS(ENABLED)
OPENTIME(FIRSTREF)
ADD(NO)
BROWSE(YES)
DELETE(NO)
READ(YES)
UPDATE(NO)
The ACCTFIL file definition specifies READ(YES) and UPDATE(NO) because this is an inquiry-only transaction. The file cannot be modified through this transaction, providing defense-in-depth against programming errors.
Compilation JCL
//AINQCOMP JOB (ACCT),'COMPILE AINQ',CLASS=A,
// MSGCLASS=X,NOTIFY=&SYSUID
//*
//* STEP 1: ASSEMBLE BMS MAP (PHYSICAL)
//*
//MAPPHYS EXEC PGM=DFHMAPT
//SYSIN DD DSN=BANK.BMS.SOURCE(AINQMS),DISP=SHR
//SYSPRINT DD SYSOUT=*
//SYSPUNCH DD DSN=BANK.CICS.LOADLIB(AINQMS),DISP=SHR
//*
//* STEP 2: ASSEMBLE BMS MAP (SYMBOLIC)
//*
//MAPSYMB EXEC PGM=DFHMAPT
//SYSIN DD DSN=BANK.BMS.SOURCE(AINQMS),DISP=SHR
//SYSPRINT DD SYSOUT=*
//SYSPUNCH DD DSN=BANK.COBOL.COPYLIB(AINQMS),DISP=SHR
//*
//* STEP 3: TRANSLATE CICS COMMANDS
//*
//TRANSLAT EXEC PGM=DFHECP1$,
// PARM='COBOL3,SP'
//SYSIN DD DSN=BANK.COBOL.SOURCE(AINQPROG),DISP=SHR
//SYSPRINT DD SYSOUT=*
//SYSPUNCH DD DSN=&&TRANS,DISP=(NEW,PASS),
// SPACE=(TRK,(5,5))
//*
//* STEP 4: COMPILE COBOL
//*
//COMPILE EXEC PGM=IGYCRCTL,
// PARM='RENT,APOST,DATA(24),NODYNAM'
//SYSIN DD DSN=&&TRANS,DISP=(OLD,DELETE)
//SYSLIB DD DSN=BANK.COBOL.COPYLIB,DISP=SHR
// DD DSN=CICSTS56.CICS.SDFHCOB,DISP=SHR
//SYSLIN DD DSN=&&OBJ,DISP=(NEW,PASS),
// SPACE=(TRK,(5,5))
//SYSPRINT DD SYSOUT=*
//SYSUT1 DD SPACE=(CYL,(1,1))
//SYSUT2 DD SPACE=(CYL,(1,1))
//SYSUT3 DD SPACE=(CYL,(1,1))
//SYSUT4 DD SPACE=(CYL,(1,1))
//SYSUT5 DD SPACE=(CYL,(1,1))
//SYSUT6 DD SPACE=(CYL,(1,1))
//SYSUT7 DD SPACE=(CYL,(1,1))
//*
//* STEP 5: LINK-EDIT
//*
//LKED EXEC PGM=IEWL,
// PARM='RENT,LIST,MAP,XREF'
//SYSLIN DD DSN=&&OBJ,DISP=(OLD,DELETE)
// DD DDNAME=SYSLKIN
//SYSLKIN DD *
INCLUDE SYSLIB(DFHELII)
NAME AINQPROG(R)
/*
//SYSLIB DD DSN=CEE.SCEELKED,DISP=SHR
// DD DSN=CICSTS56.CICS.SDFHLOAD,DISP=SHR
//SYSLMOD DD DSN=BANK.CICS.LOADLIB,DISP=SHR
//SYSPRINT DD SYSOUT=*
Lessons Learned
The AINQ transaction has been in production at Heartland Community Bank for over a year, handling an average of 12,000 inquiries per day. The key lessons from the project:
-
Pseudo-conversational design is non-negotiable. An early prototype used conversational mode. With 350 terminals, CICS hit its MAXTASK limit during peak hours, causing transaction queuing and teller complaints. Switching to pseudo-conversational reduced the average concurrent task count from 200 to 8.
-
Validate before file access. The input validation paragraph (2200-VALIDATE-ACCT-NO) prevents unnecessary VSAM reads. Before this validation was added, approximately 15% of inquiries were for invalid account numbers, wasting I/O on NOTFND reads.
-
Do not store file data in the COMMAREA when freshness matters. An earlier design cached the account record in the COMMAREA to avoid re-reading on PF5 refresh. This caused tellers to see stale balances -- a deposit posted by another teller since the last inquiry was invisible until the first teller re-entered the account number. Re-reading on every display guarantees current data.
-
Use RESP instead of HANDLE CONDITION. The original programmer used HANDLE CONDITION with paragraph branching. During a maintenance change, a new error condition fell through to the wrong handler, causing mysterious screen corruption. Rewriting all error handling to use inline RESP checks eliminated the control-flow confusion and made the code easier to follow.
-
The ERASE vs. DATAONLY decision matters for user experience. Using ERASE on every SEND MAP caused a visible screen flicker. Switching to DATAONLY for error message updates (where only the error line changes) eliminated the flicker and made the application feel more responsive. ERASE is reserved for full-screen transitions.