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

  1. The teller types transaction ID "AINQ" and presses Enter to start the inquiry
  2. The system displays an empty inquiry screen with the cursor on the account number field
  3. The teller enters a 10-digit account number and presses Enter
  4. 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
  5. The teller can enter a new account number to inquire on another account
  6. PF3 exits the transaction
  7. PF5 refreshes the current account display
  8. 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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.