Case Study 2: MedClaim's Claim Status Lookup Screen

Background

MedClaim Health Services' call center receives approximately 3,000 calls per day. The number one question: "What is the status of my claim?" Before CICS, customer service representatives accessed claim information through a batch-generated report printed nightly. If a claim was submitted after the report ran, the representative could only say "It is being processed."

James Okafor's team was tasked with building a real-time claim status inquiry (transaction CINQ) that could display the current status of any claim, including those submitted that same day.

System Design

The Screen Layout

MedClaim's claim status screen needed to display significantly more information than GlobalBank's simple account inquiry:

Row 1:  MEDCLAIM CLAIM STATUS INQUIRY          Date  Time
Row 3:  Claim Number: [______________]
Row 5:  Status:       APPROVED          Adjud Date: 2024-11-01
Row 7:  Member:       John Smith        Member ID:  M00123456
Row 9:  Provider:     Dr. Jane Wilson   Network:    In-Network
Row 11: Service Code: 99213             Service Date: 2024-10-15
Row 13: Amount Billed:    $  150.00
Row 14: Amount Allowed:   $  125.00
Row 15: Member Copay:     $   25.00
Row 16: Amount Paid:      $  100.00
Row 18: Denial Reason: N/A
Row 21: Enter=Lookup  PF3=Exit  PF5=Print  PF7=History
Row 23: Transaction: CINQ

The Multi-Table Challenge

Unlike GlobalBank's single-table inquiry, MedClaim's claim status requires data from four tables:

  1. CLAIM — claim number, status, amounts, dates
  2. MEMBER — member name, ID
  3. PROVIDER — provider name, network status
  4. PAYMENT — payment amounts, copay, adjustments

Sarah Kim insisted that all data display in a single screen, even though it required a four-table JOIN:

SELECT C.CLAIM_NUMBER, C.CLAIM_STATUS,
       C.CLAIM_AMOUNT, C.SERVICE_CODE,
       C.SERVICE_DATE, C.SUBMIT_DATE,
       C.ADJUD_DATE, C.ADJUD_REASON,
       M.MEMBER_NAME, M.MEMBER_ID,
       P.PROVIDER_NAME, P.NETWORK_STATUS,
       PY.AMOUNT_ALLOWED, PY.MEMBER_COPAY,
       PY.AMOUNT_PAID
FROM CLAIM C
JOIN MEMBER M ON C.MEMBER_ID = M.MEMBER_ID
JOIN PROVIDER P ON C.PROVIDER_ID = P.PROVIDER_ID
LEFT JOIN PAYMENT PY ON C.CLAIM_NUMBER = PY.CLAIM_NUMBER
WHERE C.CLAIM_NUMBER = :WS-CLAIM-NUM

The LEFT JOIN on PAYMENT was critical — not all claims have payment records (pending and denied claims do not). This required indicator variables for all PAYMENT columns.

The PF7 Challenge: Claim History

Sarah wanted PF7 to display the claim's processing history — a timeline of every status change. This required a second screen, which meant the transaction needed multi-screen state management via the COMMAREA:

       01  WS-COMMAREA.
           05  WS-CA-STATE         PIC X(1).
               88  WS-CA-INQUIRY           VALUE 'I'.
               88  WS-CA-HISTORY           VALUE 'H'.
           05  WS-CA-CLAIM-NUM    PIC X(15).
           05  WS-CA-HIST-POS     PIC S9(4) COMP.

When PF7 is pressed: 1. Save the current claim number and state in COMMAREA 2. Query the CLAIM_HISTORY table for status changes 3. SEND a different map (CLMHIST instead of CLMSTAT) 4. On the history screen, PF3 returns to the inquiry screen using the saved claim number

Technical Challenges

Challenge 1: Null Handling for Pending Claims

Pending claims have NULL values for ADJUD_DATE, ADJUD_REASON, AMOUNT_ALLOWED, MEMBER_COPAY, and AMOUNT_PAID. Without indicator variables, these NULLs caused SQLCODE -305 abends. The fix required indicator variables for every nullable column:

       01  WS-INDICATORS.
           05  WS-IND-ADJUD-DATE   PIC S9(4) COMP.
           05  WS-IND-ADJUD-REASON PIC S9(4) COMP.
           05  WS-IND-AMT-ALLOWED  PIC S9(4) COMP.
           05  WS-IND-COPAY        PIC S9(4) COMP.
           05  WS-IND-AMT-PAID     PIC S9(4) COMP.

For each field, the display logic checked the indicator:

           IF WS-IND-AMT-PAID = -1
               MOVE 'Pending' TO AMTPAIDO
           ELSE
               MOVE WS-AMT-PAID TO WS-FMT-AMT
               MOVE WS-FMT-AMT TO AMTPAIDO
           END-IF

Challenge 2: Performance Under Load

Initial testing showed response times of 0.8 seconds — within the 1-second target. But during load testing with 500 concurrent users, response time spiked to 3.2 seconds. Tomás Rivera identified the cause: the four-table JOIN was causing excessive lock contention because the default isolation level (CS) held locks during the fetch.

Solution: Since this is a read-only inquiry, the query was changed to WITH UR:

SELECT ... FROM CLAIM C ... WHERE C.CLAIM_NUMBER = ? WITH UR

Response time under load dropped to 0.3 seconds.

Challenge 3: The Stale Screen Problem

Representatives sometimes left the claim status screen displayed while on a long call. When they looked at it 20 minutes later, the data was stale — the claim might have been adjudicated during that time. PF5 ("Refresh") was added to re-query and redisplay the current data without the representative needing to retype the claim number:

           WHEN DFHPF5
               IF WS-CA-CLAIM-NUM NOT = SPACES
                   PERFORM 2000-LOOKUP-CLAIM
               ELSE
                   MOVE 'No claim to refresh' TO MSGO
                   PERFORM 1500-SEND-DATAONLY
               END-IF

The Modernization Path

Two years after the CICS screen was deployed, MedClaim launched a member web portal. Instead of building new query logic, the team:

  1. Extracted the claim status logic from CINQ into a separate program (CLMLKUP) that accepted a claim number via COMMAREA and returned the data
  2. The CICS screen program (CINQ) used LINK to call CLMLKUP
  3. A new CICS Liberty servlet also used LINK to call the same CLMLKUP
  4. The web portal called the servlet via REST

The core COBOL logic — the SQL query, null handling, and data formatting — was written once and served both channels.

Results

Metric Before After
Claim status calls 1,200/day 400/day (67% reduction)
Average response time Next-day report 0.3 seconds
Call handle time for status inquiries 3.5 minutes 1.2 minutes
Web portal self-service lookups 0/day 2,500/day
COBOL programs serving web portal 0 1 (shared with CICS)

Discussion Questions

  1. The LEFT JOIN on PAYMENT was necessary because not all claims have payment records. What would happen if an INNER JOIN was used instead? How would this affect the user experience?
  2. Why was extracting the lookup logic into a separate program (CLMLKUP) a better architecture than having the servlet directly access DB2? What does this have to do with the Modernization Spectrum theme?
  3. The COMMAREA stores claim number and state but not the full claim data. Why not store the entire claim record in the COMMAREA to avoid re-querying when navigating between screens?
  4. How would you handle a scenario where a claim number entered by a representative matches multiple claims (e.g., if the same provider submitted duplicate claims)? What screen design changes would be needed?