Capstone: claims end-of-day batch
This is the kind of program you'll maintain in your first mainframe job. An insurer receives claims all day. Every night a batch job sorts them, checks each one against the policy master, works out what to pay, writes a report for the claims managers and a reject file for the data-quality team, and sets a return code the scheduler can act on.
You'll write CLMPROC, the program at the centre of that job. It uses
nearly everything in the course:
| Skill | Where it came from |
|---|---|
| Copybooks for record layouts | subprograms module |
| Reading and writing sequential datasets, DD names | sequential files |
Loading a table and SEARCH ALL |
tables |
NUMERIC checks before arithmetic |
this module (S0C7) |
| Edited money pictures, print lines | reports |
| Control break on a sorted key | reports |
PARM, return codes, multi-step JCL with COND |
JCL |
How the job fits together
CLAIMS.DAILY.INPUT ──SORT──> &&SORTED ──CLMPROC──> CLAIMS.EOD.REPORT ──IEBGENER──> SYSOUT
CLAIMS.POLICY.TABLE ─┘ └──> CLAIMS.EOD.REJECTS
Your program never sorts anything. It trusts that SORTCLM delivered the
claims in region order, which is what makes a one-pass control break
possible. The last step prints the report only when CLMPROC ended with
RC 4 or less (COND=(4,LT)), so a bad run date (RC 16) stops the job
before a wrong report reaches anyone.
Looking up the policy
Load the policy file into a table once, then look up every claim in
memory. SEARCH ALL does a binary search, and when it finds a match it
leaves the index pointing at the entry, so you can keep using it:
IDENTIFICATION DIVISION.
PROGRAM-ID. POLFIND.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-POLICY-DATA PIC X(27)
VALUE "P0000101AP0000103LP0000106A".
01 WS-POLICY-TABLE REDEFINES WS-POLICY-DATA.
05 WS-POLICY OCCURS 3 TIMES
ASCENDING KEY IS TBL-POL-NO
INDEXED BY POL-IX.
10 TBL-POL-NO PIC X(8).
10 TBL-POL-STATUS PIC X.
01 WS-WANTED PIC X(8).
PROCEDURE DIVISION.
MOVE "P0000103" TO WS-WANTED
PERFORM FIND-POLICY
MOVE "P0000104" TO WS-WANTED
PERFORM FIND-POLICY
STOP RUN.
FIND-POLICY.
SEARCH ALL WS-POLICY
AT END
DISPLAY WS-WANTED " NOT FOUND"
WHEN TBL-POL-NO (POL-IX) = WS-WANTED
DISPLAY WS-WANTED " STATUS "
TBL-POL-STATUS (POL-IX)
END-SEARCH.
P0000103 STATUS L
P0000104 NOT FOUND
In the capstone the table is OCCURS 1 TO 50 DEPENDING ON
WS-POLICY-COUNT, so the search only covers the entries you loaded.
Things that trip people up
- Validate first, calculate second. On z/OS a claim with letters in
its amount would abend the whole night's run with S0C7. Nothing touches
CLM-AMOUNTarithmetically until it has passedNUMERIC. - Break on accepted claims only. Rejected claims never reach the
report, so a region whose claims were all rejected must not produce a
total line. Do the break check inside the paragraph that accepts a
claim, not straight after the
READ. - Clear output records. A record area in the
FILE SECTIONisn't initialised. The starter doesMOVE SPACES TO REJECT-LINEbefore filling it; without that GnuCOBOL refuses to write the stray bytes (status 71), and on z/OS they would go into the file. - The last group. The final region's total is written after the read loop ends. The starter's main line already does this.
Build it a piece at a time and submit often: the visible tests show your report and reject file next to the expected ones, and the job log shows each step's return code.
On the job
When you talk about this project in an interview, describe it the way an operations team would: "a three-step batch job: DFSORT, a COBOL validation and pricing program with a table lookup and control-break report, and a print step conditioned on the return code. Bad input goes to a reject file with reason codes; RC 4 flags rejects, RC 16 stops the job." That sentence shows you understand batch, not just syntax.
Your task
Write CLMPROC, the heart of the insurer's claims end-of-day job.
Operations run it with this JCL (see the JCL tab):
SORTCLMsorts the day's claims by region, then claim id, into&&SORTED.PROCESSruns your program withPARM='20261024', the run date.PRTRPTprints the report, unlessPROCESSended worse than RC 4.
DD names (all line sequential; record layouts are the copybooks
CLAIMREC and POLREC, see the Copybooks tab):
| DD | Contents |
|---|---|
CLAIMS |
sorted claims, COPY CLAIMREC (40 bytes) |
POLICIES |
policy table, ascending POL-NO, at most 50 records, COPY POLREC |
CLMRPT |
the report, 80-byte lines |
REJECTS |
rejected claims: claim id, space, reason |
Run date. Read PARM with ACCEPT WS-PARM FROM COMMAND-LINE. If it
is not numeric, display CLMPROC INVALID RUN DATE: and the PARM, and
stop with return code 16 before opening anything.
Validation. Load the policy table first. Then check each claim in this order; the first failure is the reason:
| Check | Reason |
|---|---|
CLM-AMOUNT not numeric |
INVALID AMOUNT |
CLM-TYPE not A, H or T |
INVALID TYPE |
CLM-DATE not numeric, or after the run date |
INVALID DATE |
CLM-POLICY not in the table |
POLICY NOT FOUND |
policy status not A |
POLICY LAPSED |
Pricing. Payable = claimed amount minus the policy's POL-EXCESS,
but never below 0 and never above POL-LIMIT.
Report. The starter already writes the title, headings, detail
lines and grand total. Add a control break on region: after the
last accepted claim of each region, write the region total line
(RPT-REGION-TOTAL) followed by one blank line. Regions with no
accepted claims get no total line.
CLAIMS END-OF-DAY REPORT RUN DATE 2026-10-24
REGION CLAIM ID POLICY TYPE CLAIMED PAYABLE
CRK C0000002 P0000102 HOME 12,000.00 11,500.00
TOTAL REGION CRK CLAIMS 1 12,000.00 11,500.00
DUB C0000001 P0000101 AUTO 1,250.00 1,000.00
...
Return code: 4 if any claim was rejected, otherwise 0.