Gold Standard PLM (v3 hybrid: L0 spine + unified-STB grounded). Archetype: batch_report. Program kind: batch.
CBTRN03C is a batch COBOL program whose stated function is to "Print the transaction detail report" (per the source header comment block, L2–L5). It reads a date-filtered, card-sorted daily transaction file, enriches each transaction with descriptive lookups (card-account cross-reference, transaction type, transaction category), and writes a paginated Daily Transaction Report (REPT-LONG-NAME VALUE 'Daily Transaction Report', L178–L179) to a sequential report dataset. The report carries page headers, per-transaction detail lines, per-card account subtotals, per-page subtotals, and a grand total (per STB data_division.working_storage.fields[]; report line records REPORT-NAME-HEADER, TRANSACTION-DETAIL-REPORT, REPORT-PAGE-TOTALS, REPORT-ACCOUNT-TOTALS, REPORT-GRAND-TOTALS, L175–L237).
The program is classified batch_report and its lineage tier is arithmetic (per STB metrics / spine header), reflecting that its principal computed logic is the additive roll-up of transaction amounts into page, account, and grand totals.
CBTRN03C runs as a batch job step under JCL job TRANREPT, step STEP10R (per STB jcl_facts.tier1_core.invocation; the JCL deck is jcl/TRANREPT.jcl, per spine INVOCATION_CONTEXT). It is the terminal (third) step of a three-step pipeline (per jcl_facts.tier2_enhanced.step_sequence):
STEP05R.PRC001 — IDCAMS (transaction VSAM → sequential backup)STEP05R — SORT (backup → date-filtered, card-sorted daily file)STEP10R — CBTRN03C (this program)STEP05R (program SORT), whose SORTOUT DD feeds this step's TRANFILE DD (per jcl_facts.tier2_enhanced.pipeline.upstream_steps[]). There are no downstream steps (pipeline.downstream_steps = []).jcl_facts.tier2_enhanced.prerequisites[]): the CARDXREF, TRANTYPE, and TRANCATG VSAM lookup files and the DATEPARM parameter file.STEP05R SORT that produces its input; it is the last of the three sequential steps (per step_sequence, order 3).TRANREPT, notify &SYSUID, per jcl_facts.tier1_core.invocation). No run frequency is asserted by the STB and none is inferred here.A key operational fact: the upstream SORT step (STEP05R) applies a DFSORT INCLUDE COND that pre-filters the transaction file to records whose processing date lies between the start and end parameter dates, and sorts ascending by card number. The raw SORT control statement (from jcl_facts.tier1_core.sort_specifications.raw) is:
SORT FIELDS=(TRAN-CARD-NUM,A)
INCLUDE COND=(TRAN-PROC-DT,GE,PARM-START-DATE,AND,
TRAN-PROC-DT,LE,PARM-END-DATE)
The sort key is the card-number field and the filter bounds the processing-date field to the parameter date window (these are DFSORT symbolic field names, not COBOL entities). This means the input reaching CBTRN03C is already date-bounded and card-ordered — context that matters for the in-program date check and control-break logic discussed in s05 and s11.
CBTRN03C touches 6 files — 5 read (INPUT) and 1 written (OUTPUT) (per spine DATA_FLOW FILE ROLE COUNTS, counted by data direction):
| File (FD) | Assign / DD | Organization | Direction | Record area |
|---|---|---|---|---|
TRANSACT-FILE
|
TRANFILE | SEQUENTIAL | input (primary driver) |
TRAN-RECORD
|
XREF-FILE
|
CARDXREF | INDEXED, RANDOM | input (lookup) |
CARD-XREF-RECORD
|
TRANTYPE-FILE
|
TRANTYPE | INDEXED, RANDOM | input (lookup) |
TRAN-TYPE-RECORD
|
TRANCATG-FILE
|
TRANCATG | INDEXED, RANDOM | input (lookup) |
TRAN-CAT-RECORD
|
DATE-PARMS-FILE
|
DATEPARM | SEQUENTIAL | input (parameter) |
WS-DATEPARM-RECORD
|
REPORT-FILE
|
TRANREPT | SEQUENTIAL | output (report) |
FD-REPTFILE-REC
|
The SELECT/FILE-CONTROL declarations are at L29–L57; the FD record areas at L61–L88 (per STB environment_division / data_division.file_descriptions). The primary input TRANSACT-FILE is read sequentially in the main loop; the three VSAM KSDS files (XREF-FILE, TRANTYPE-FILE, TRANCATG-FILE) are read RANDOM by key for per-transaction enrichment; DATE-PARMS-FILE is read once for the reporting date window; REPORT-FILE receives every report line via WRITE FD-REPTFILE-REC (L477).
CBTRN03C is invoked by job TRANREPT, step STEP10R, executing PGM=CBTRN03C (per jcl_facts.tier1_core.invocation). Job-level attributes: MSGCLASS=0, NOTIFY=&SYSUID, JCLLIB ORDER=(AWS.M2.CARDDEMO.PROC). No step PARM is passed (invocation.parm = null) — the reporting date window is supplied through the DATEPARM dataset rather than a PARM string.
DD statements for STEP10R (per jcl_facts.tier1_core.dd_mappings):
| DD | Dataset | DISP | Type | Notes |
|---|---|---|---|---|
| STEPLIB | AWS.M2.CARDDEMO.LOADLIB | SHR | Sequential | load library |
| TRANFILE | AWS.M2.CARDDEMO.TRANSACT.DALY(+1) | SHR | Sequential (GDG) | primary input, fed by STEP05R SORTOUT |
| CARDXREF | AWS.M2.CARDDEMO.CARDXREF.VSAM.KSDS | SHR | VSAM | card cross-reference lookup |
| TRANTYPE | AWS.M2.CARDDEMO.TRANTYPE.VSAM.KSDS | SHR | VSAM | transaction-type lookup |
| TRANCATG | AWS.M2.CARDDEMO.TRANCATG.VSAM.KSDS | SHR | VSAM | transaction-category lookup |
| DATEPARM | AWS.M2.CARDDEMO.DATEPARM | SHR | Sequential | reporting date parameters |
| TRANREPT | AWS.M2.CARDDEMO.TRANREPT(+1) | NEW,CATLG,DELETE | Sequential (GDG) | report output; LRECL=133, RECFM=FB, SPACE=(CYL,(1,1),RLSE), UNIT=SYSDA |
| SYSOUT / SYSPRINT | (SYSOUT=*) | — | — | system output |
The report output TRANREPT(+1) is a new generation-data-group dataset catalogued on normal completion and deleted on abnormal completion (DISP=(NEW,CATLG,DELETE)), with fixed-block 133-byte records — consistent with the 133-byte report record FD-REPTFILE-REC (L85).
The following record layouts are transcribed verbatim from the deterministic spine RECORD_BYTE_LAYOUT block (OCCURS already expanded, USAGE packing already applied; offsets are 0-based within the 01-record). These are the authoritative byte layouts for the persisted / read-into records.
TRAN-RECORD (350 bytes) — copybook CVTRA05Y, read area for TRANSACT-FILE| Field | PIC | Offset | Bytes | Dir |
|---|---|---|---|---|
TRAN-RECORD
|
—
|
0 | 350 | unused |
TRAN-ID
|
X(16)
|
0 | 16 | in |
TRAN-TYPE-CD
|
X(02)
|
16 | 2 | in |
TRAN-CAT-CD
|
9(04)
|
18 | 4 | in |
TRAN-SOURCE
|
X(10)
|
22 | 10 | in |
TRAN-DESC
|
X(100)
|
32 | 100 | unused |
TRAN-AMT
|
S9(09)V99
|
132 | 11 | in |
TRAN-MERCHANT-ID
|
9(09)
|
143 | 9 | unused |
TRAN-MERCHANT-NAME
|
X(50)
|
152 | 50 | unused |
TRAN-MERCHANT-CITY
|
X(50)
|
202 | 50 | unused |
TRAN-MERCHANT-ZIP
|
X(10)
|
252 | 10 | unused |
TRAN-CARD-NUM
|
X(16)
|
262 | 16 | in |
TRAN-ORIG-TS
|
X(26)
|
278 | 26 | unused |
TRAN-PROC-TS
|
X(26)
|
304 | 26 | in |
FILLER
|
X(20)
|
330 | 20 | unused |
CARD-XREF-RECORD (50 bytes) — copybook CVACT03Y, read area for XREF-FILE| Field | PIC | Offset | Bytes | Dir |
|---|---|---|---|---|
CARD-XREF-RECORD
|
—
|
0 | 50 | unused |
XREF-CARD-NUM
|
X(16)
|
0 | 16 | unused |
XREF-CUST-ID
|
9(09)
|
16 | 9 | unused |
XREF-ACCT-ID
|
9(11)
|
25 | 11 | in |
FILLER
|
X(14)
|
36 | 14 | unused |
TRAN-TYPE-RECORD (60 bytes) — copybook CVTRA03Y, read area for TRANTYPE-FILE| Field | PIC | Offset | Bytes | Dir |
|---|---|---|---|---|
TRAN-TYPE-RECORD
|
—
|
0 | 60 | unused |
TRAN-TYPE
|
X(02)
|
0 | 2 | unused |
TRAN-TYPE-DESC
|
X(50)
|
2 | 50 | in |
FILLER
|
X(08)
|
52 | 8 | unused |
TRAN-CAT-RECORD (60 bytes) — copybook CVTRA04Y, read area for TRANCATG-FILE| Field | PIC | Offset | Bytes | Dir |
|---|---|---|---|---|
TRAN-CAT-RECORD
|
—
|
0 | 60 | unused |
TRAN-CAT-KEY
|
—
|
0 | 6 | unused |
TRAN-TYPE-CD
|
X(02)
|
0 | 2 | in |
TRAN-CAT-CD
|
9(04)
|
2 | 4 | in |
TRAN-CAT-TYPE-DESC
|
X(50)
|
6 | 50 | in |
FILLER
|
X(04)
|
56 | 4 | unused |
WS-DATEPARM-RECORD (21 bytes) — read area for DATE-PARMS-FILE| Field | PIC | Offset | Bytes | Dir |
|---|---|---|---|---|
WS-DATEPARM-RECORD
|
—
|
0 | 21 | unused |
WS-START-DATE
|
X(10)
|
0 | 10 | in |
FILLER
|
X(01)
|
10 | 1 | unused |
WS-END-DATE
|
X(10)
|
11 | 10 | in |
The remaining FD record areas are fixed byte strings: FD-REPTFILE-REC PIC X(133) (L85, the report line) and FD-DATEPARM-REC PIC X(80) (L88). The VSAM key fields declared in FILE-CONTROL are FD-XREF-CARD-NUM (L36), FD-TRAN-TYPE (L42), and FD-TRAN-CAT-KEY (L48).
| 01-level group | Role | Notable fields (L) |
|---|---|---|
WS-DATEPARM-RECORD
|
Reporting date window |
WS-START-DATE, WS-END-DATE (L254–L257) |
WS-REPORT-VARS
|
Report state + accumulators |
WS-FIRST-TIME VALUE 'Y', WS-LINE-COUNTER COMP-3 VALUE 0, WS-PAGE-SIZE COMP-3 VALUE 20, WS-PAGE-TOTAL / WS-ACCOUNT-TOTAL / WS-GRAND-TOTAL S9(09)V99 VALUE 0, WS-CURR-CARD-NUM VALUE SPACES (L259–L269) |
APPL-RESULT
|
I/O result code (S9(9) COMP) | 88 APPL-AOK VALUE 0; 88 APPL-EOF VALUE 16 (L282–L284) |
END-OF-FILE
|
Loop sentinel (X(01) VALUE 'N') |
drives PERFORM UNTIL (L286) |
IO-STATUS, IO-STATUS-04, TWO-BYTES-BINARY |
File-status display formatting | used by 9910-DISPLAY-IO-STATUS (L271–L280) |
ABCODE, TIMING |
Abend parameters (S9(9) BINARY) | passed to CEE3ABD (L287–L288) |
| Report line records | Report images |
REPORT-NAME-HEADER, TRANSACTION-DETAIL-REPORT, TRANSACTION-HEADER-1, TRANSACTION-HEADER-2, REPORT-PAGE-TOTALS, REPORT-ACCOUNT-TOTALS, REPORT-GRAND-TOTALS (L175–L237) |
Six file-status two-byte groups back the six files: TRANFILE-STATUS, CARDXREF-STATUS, TRANTYPE-STATUS, TRANCATG-STATUS, TRANREPT-STATUS, DATEPARM-STATUS (per FILE-CONTROL FILE STATUS IS clauses, L31–L57; group definitions L116–L252).
WS-PAGE-SIZE PIC 9(03) COMP-3 VALUE 20 (L263–L264). The page-break test fires when WS-LINE-COUNTER is an exact multiple of this value (see s05 / s12).WS-PAGE-TOTAL, WS-ACCOUNT-TOTAL, WS-GRAND-TOTAL are PIC S9(09)V99 VALUE 0 (L266–L268); each holds a signed 9-digit-plus-2-decimal running total.WS-LINE-COUNTER PIC 9(09) COMP-3 VALUE 0 (L261–L262).APPL-RESULT carries the discrete result codes checked via its 88-levels: APPL-AOK when the field equals '0', APPL-EOF when it equals '16' (verbatim VALUE enumeration per data_division.working_storage.fields[].value_literals, L282–L284).No JCL PARM string is supplied to STEP10R (jcl_facts.tier1_core.invocation.parm = null). Runtime parameterization is instead by dataset: DATE-PARMS-FILE (DD DATEPARM) is read into WS-DATEPARM-RECORD, populating WS-START-DATE and WS-END-DATE (the 10-byte start date, a 1-byte filler, and the 10-byte end date; L254–L257, read at L353).
Total: 26 paragraphs (per STB procedure_division.paragraphs.length). Lines are expanded-source coordinates; end lines are source-derived (source-derived — STB extraction gap: paragraph end line; not in unified STB is not the case here — end_line IS carried at procedure_division.paragraphs[].end_line).
| Paragraph | Lines | Role |
|---|---|---|
| (unnamed PROCEDURE DIVISION driver) | L291–L349 | Main sequence: opens, date read, PERFORM UNTIL loop, closes, GOBACK |
0000-TRANFILE-OPEN
|
L508–L525 | OPEN INPUT TRANSACT-FILE |
0100-REPTFILE-OPEN
|
L526–L543 | OPEN OUTPUT REPORT-FILE |
0200-CARDXREF-OPEN
|
L544–L561 | OPEN INPUT XREF-FILE |
0300-TRANTYPE-OPEN
|
L562–L579 | OPEN INPUT TRANTYPE-FILE |
0400-TRANCATG-OPEN
|
L580–L597 | OPEN INPUT TRANCATG-FILE |
0500-DATEPARM-OPEN
|
L598–L615 | OPEN INPUT DATE-PARMS-FILE |
0550-DATEPARM-READ
|
L352–L379 | READ date parameters; set EOF or abend |
1000-TRANFILE-GET-NEXT
|
L380–L405 | READ next transaction; classify status |
1100-WRITE-TRANSACTION-REPORT
|
L406–L424 | First-time headers, page break, add to totals, write detail |
1110-WRITE-PAGE-TOTALS
|
L425–L437 | Emit page subtotal; roll page→grand; reset page total |
1120-WRITE-ACCOUNT-TOTALS
|
L438–L449 | Emit per-card account subtotal; reset account total |
1110-WRITE-GRAND-TOTALS
|
L450–L455 | Emit grand total |
1120-WRITE-HEADERS
|
L456–L474 | Emit report name, blank, column headers, rule line |
1111-WRITE-REPORT-REC
|
L475–L492 | WRITE one report record; check status; abend on error |
1120-WRITE-DETAIL
|
L493–L507 | Build and write one transaction detail line |
1500-A-LOOKUP-XREF
|
L616–L625 | RANDOM read XREF-FILE by card number |
1500-B-LOOKUP-TRANTYPE
|
L626–L635 | RANDOM read TRANTYPE-FILE by type code |
1500-C-LOOKUP-TRANCATG
|
L636–L645 | RANDOM read TRANCATG-FILE by type+category key |
9000-TRANFILE-CLOSE
|
L646–L663 | CLOSE TRANSACT-FILE |
9100-REPTFILE-CLOSE
|
L664–L682 | CLOSE REPORT-FILE |
9200-CARDXREF-CLOSE
|
L683–L700 | CLOSE XREF-FILE |
9300-TRANTYPE-CLOSE
|
L701–L718 | CLOSE TRANTYPE-FILE |
9400-TRANCATG-CLOSE
|
L719–L736 | CLOSE TRANCATG-FILE |
9500-DATEPARM-CLOSE
|
L737–L757 | CLOSE DATE-PARMS-FILE |
9999-ABEND-PROGRAM
|
L758–L764 | DISPLAY, set abend code, CALL CEE3ABD |
9910-DISPLAY-IO-STATUS
|
L765–L781 | Format and DISPLAY file status |
PROCEDURE DIVISION (L291-L349)
├── 0000-TRANFILE-OPEN
├── 0100-REPTFILE-OPEN
├── 0200-CARDXREF-OPEN
├── 0300-TRANTYPE-OPEN
├── 0400-TRANCATG-OPEN
├── 0500-DATEPARM-OPEN
├── 0550-DATEPARM-READ
│ └── 9910-DISPLAY-IO-STATUS ──► 9999-ABEND-PROGRAM ──► CALL CEE3ABD
├── PERFORM UNTIL END-OF-FILE = 'Y' (L302-L338)
│ ├── 1000-TRANFILE-GET-NEXT
│ ├── 1120-WRITE-ACCOUNT-TOTALS (on card control break, WS-FIRST-TIME='N')
│ │ └── 1111-WRITE-REPORT-REC
│ ├── 1500-A-LOOKUP-XREF
│ ├── 1500-B-LOOKUP-TRANTYPE
│ ├── 1500-C-LOOKUP-TRANCATG
│ ├── 1100-WRITE-TRANSACTION-REPORT (per-record path)
│ │ ├── 1120-WRITE-HEADERS ──► 1111-WRITE-REPORT-REC
│ │ ├── 1110-WRITE-PAGE-TOTALS ──► 1111-WRITE-REPORT-REC
│ │ └── 1120-WRITE-DETAIL ──► 1111-WRITE-REPORT-REC
│ ├── 1110-WRITE-PAGE-TOTALS (EOF close-out path)
│ └── 1110-WRITE-GRAND-TOTALS (EOF close-out path)
├── 9000-TRANFILE-CLOSE
├── 9100-REPTFILE-CLOSE
├── 9200-CARDXREF-CLOSE
├── 9300-TRANTYPE-CLOSE
├── 9400-TRANCATG-CLOSE
└── 9500-DATEPARM-CLOSE
Every I/O and write routine reaches 9910-DISPLAY-IO-STATUS and 9999-ABEND-PROGRAM on an error branch; those two edges are elided from the tree above for readability and detailed in s08.
The driver (L291–L349) runs a fixed open sequence, reads the date parameters once, then enters the single read loop and finally closes all files:
OPEN paragraphs run in order (0000-TRANFILE-OPEN … 0500-DATEPARM-OPEN, L293–L298), each validating its file status and abending on failure.0550-DATEPARM-READ (L300) reads DATE-PARMS-FILE into WS-DATEPARM-RECORD, sets APPL-RESULT from the status via an EVALUATE, and either displays the reporting window (APPL-AOK), sets END-OF-FILE='Y' (APPL-EOF, empty parameter file), or abends (other status).PERFORM UNTIL END-OF-FILE = 'Y' (L302–L338) is the one loop construct in the program (per spine CONTROL_STRUCTURE_COUNTS: PERFORM UNTIL loops: 1 at L302). Per iteration, guarded by IF END-OF-FILE = 'N' (L303):
1000-TRANFILE-GET-NEXT (L304) reads the next transaction into TRAN-RECORD.IF TRAN-PROC-TS (1:10) >= WS-START-DATE AND TRAN-PROC-TS (1:10) <= WS-END-DATE → CONTINUE; ELSE → NEXT SENTENCE. (See the NEXT SENTENCE analysis below — this branch does not simply skip the record.)IF END-OF-FILE = 'N' (L311) splits the per-record path (then) from the EOF close-out path (else, L329):
DISPLAY TRAN-RECORD; on a card-number control break (WS-CURR-CARD-NUM NOT= TRAN-CARD-NUM, L313) and not the first record (WS-FIRST-TIME = 'N', L314), PERFORM 1120-WRITE-ACCOUNT-TOTALS (L315); then set the current card, perform the three lookups (1500-A-LOOKUP-XREF, 1500-B-LOOKUP-TRANTYPE, 1500-C-LOOKUP-TRANCATG), and PERFORM 1100-WRITE-TRANSACTION-REPORT (L328).ADD TRAN-AMT TO WS-PAGE-TOTAL WS-ACCOUNT-TOTAL (L332), then PERFORM 1110-WRITE-PAGE-TOTALS and PERFORM 1110-WRITE-GRAND-TOTALS.CLOSE paragraphs run (L340–L345) and the program GOBACKs (L349).Per the deterministic CLOSEOUT_BRANCH_PERFORMS resolution of the L311 branch: the then (per-record) branch performs 1120-WRITE-ACCOUNT-TOTALS, 1500-A-LOOKUP-XREF, 1500-B-LOOKUP-TRANTYPE, 1500-C-LOOKUP-TRANCATG, and 1100-WRITE-TRANSACTION-REPORT; the else (EOF close-out) branch performs only1110-WRITE-PAGE-TOTALS and 1110-WRITE-GRAND-TOTALS. The account-total emitter is not on the close-out path — see the final-group omission defect below.
NEXT SENTENCE control flow (L309 — effect_class=exit_loop, danger_level=high)
NEXT SENTENCE (L309) exits the surrounding PERFORM UNTIL loop. Per STB procedure_division.control_flow.next_sentence_jumps[], this occurrence is classified effect_class=exit_loop, danger_level=high, with target_line=338. NEXT SENTENCE transfers control to the statement following the next period, which is the period on END-PERFORM. at L338 — so it jumps past the entire read loop, not to the next statement inside it. It is not equivalent to CONTINUE: maintainers refactoring this code must not substitute CONTINUE, which would fall through to the L311 IF inside the loop body instead.
jcl_facts.tier1_core.sort_specifications). Every record delivered is in range, so the CONTINUE arm is taken. The NEXT SENTENCE arm is thus operationally-safe-but-technically-misleading dead code under the standard job stream — but it is a live control-flow fact the maintainer's model needs, because outside that job stream (or with a mis-set parameter) the first out-of-range record would terminate the report prematurely.PERFORM 1000-TRANFILE-GET-NEXT returns AT END: TRANFILE-STATUS='10' → APPL-EOF → MOVE 'Y' TO END-OF-FILE (L396). Because READ ... INTO does not populate the receiving area on AT END, TRAN-RECORD (hence TRAN-PROC-TS) still holds its uninitialized value (SPACES/LOW-VALUES). The date-range test (L305) is therefore false, so the ELSE arm takes NEXT SENTENCE (L309), which jumps past END-PERFORM. (L338), exiting the loop. The subsequent IF END-OF-FILE = 'N' (L311) does not execute, so no page totals, no grand total, and no detail lines are written — the report file receives zero records and the loop body completes zero full iterations. The program falls straight through to the close sequence.'00'), passes the date gate (CONTINUE), takes the per-record path (L311 then); WS-CURR-CARD-NUM (SPACES) differs from the card number but WS-FIRST-TIME='Y' suppresses the account-total call, so headers are emitted on the first 1100-WRITE-TRANSACTION-REPORT (L407–L412) and one detail line is written. Iteration 2's 1000-TRANFILE-GET-NEXT hits AT END (END-OF-FILE='Y'); TRAN-PROC-TS still holds record 1 (in range) so the date gate takes CONTINUE, and the L311 IF now takes the else close-out, writing the page total and grand total. Note two consequences for the single-record case: the one card's account subtotal is never written (final-group omission, below) and its amount is added to the page/account totals twice (double-write, below).END-OF-FILE = 'Y' (the PERFORM UNTIL condition at L302). The sentinel END-OF-FILE (PIC X(01) VALUE 'N', L286) is set to 'Y' only inside 1000-TRANFILE-GET-NEXT when the read returns status '10' (APPL-EOF, VALUE 16) → MOVE 'Y' TO END-OF-FILE (L396), and inside 0550-DATEPARM-READ when the parameter file is empty (L368). The inner IF END-OF-FILE = 'N' guards (L303, L311) prevent processing a record on the terminating iteration.OCCURS arrays in the I/O path (per data_division.working_storage.fields[] — no OCCURS in the transaction/report records); bound-overflow is not applicable. The only running counter, WS-LINE-COUNTER (PIC 9(09) COMP-3), is used modulo WS-PAGE-SIZE for pagination, not as an array index.Per-record data movement is dominated by MOVE (per STB statements.by_verb.MOVE = 97), assembling the detail line in 1120-WRITE-DETAIL (L494–L503): INITIALIZE TRANSACTION-DETAIL-REPORT, then MOVEs of TRAN-ID, XREF-ACCT-ID, TRAN-TYPE-CD, TRAN-TYPE-DESC, TRAN-CAT-CD, TRAN-CAT-TYPE-DESC, TRAN-SOURCE, and TRAN-AMT into the corresponding TRAN-REPORT-* fields. Arithmetic is additive only (statements.by_verb.ADD = 16, SUBTRACT = 2; no COMPUTE per absence_factsno_compute): amount roll-ups (ADD TRAN-AMT TO WS-PAGE-TOTAL WS-ACCOUNT-TOTAL, L332/L419; ADD WS-PAGE-TOTAL TO WS-GRAND-TOTAL, L429) and line-counter increments (ADD 1 TO WS-LINE-COUNTER). The two SUBTRACTs are the close-routine result-code resets (SUBTRACT APPL-RESULT FROM APPL-RESULT, L650/L668). No STRING, UNSTRING, or INSPECT operations are present (absence_factsno_string_inspect).
The lone PERFORM UNTIL END-OF-FILE = 'Y' (L302) is the primary and only loop (per procedure_division.control_flow.loop_constructs[]). Its exit is driven solely by the END-OF-FILE sentinel as described under Termination signal above; there is no counter-bounded or PERFORM VARYING loop in the program.
The program performs a single-level control break on card number. The break field is TRAN-CARD-NUM compared against the retained WS-CURR-CARD-NUM (L313). On a change of card (and once past the first record, WS-FIRST-TIME='N'), 1120-WRITE-ACCOUNT-TOTALS emits the prior card's account subtotal (REPT-ACCOUNT-TOTAL) and resets WS-ACCOUNT-TOTAL to zero (L439–L442). The input arriving card-sorted (upstream SORT on TRAN-CARD-NUM,A) is what makes this single-pass control break correct.
CBTRN03C is a straightforward sequential report writer with random-access enrichment: one input driver loop, three keyed lookups per transaction, and a three-tier total roll-up (page → account → grand). All error handling is by explicit file-status checks that funnel to a hard abend (s08). Cyclomatic complexity is 44 (McCabe, per STB metrics.cyclomatic_complexity), concentrated in the repetitive per-file status-check/abend blocks rather than in the report logic. Perform nesting depth is 2 (metrics.perform_nesting_depth).
DATA_FLOW)FILE ROLE COUNTS (by data direction): 6 files — 5 INPUT (read), 1 OUTPUT (written).
| File (FD) | Ops | Paragraph(s) | Record | Direction |
|---|---|---|---|---|
| DATE-PARMS-FILE | OPEN READ CLOSE | 0500-DATEPARM-OPEN, 0550-DATEPARM-READ, 9500-DATEPARM-CLOSE | WS-DATEPARM-RECORD | input |
| TRANSACT-FILE | OPEN READ CLOSE | 0000-TRANFILE-OPEN, 1000-TRANFILE-GET-NEXT, 9000-TRANFILE-CLOSE | TRAN-RECORD | input |
| XREF-FILE | OPEN READ CLOSE | 0200-CARDXREF-OPEN, 1500-A-LOOKUP-XREF, 9200-CARDXREF-CLOSE | CARD-XREF-RECORD | input |
| TRANTYPE-FILE | OPEN READ CLOSE | 0300-TRANTYPE-OPEN, 1500-B-LOOKUP-TRANTYPE, 9300-TRANTYPE-CLOSE | TRAN-TYPE-RECORD | input |
| TRANCATG-FILE | OPEN READ CLOSE | 0400-TRANCATG-OPEN, 1500-C-LOOKUP-TRANCATG, 9400-TRANCATG-CLOSE | TRAN-CAT-RECORD | input |
| REPORT-FILE | OPEN WRITE CLOSE | 0100-REPTFILE-OPEN, 1111-WRITE-REPORT-REC, 9100-REPTFILE-CLOSE | FD-REPTFILE-REC | output |
Supplementary ASCII view (agrees with the table above; paragraph nodes shown in brackets):
DATE-PARMS-FILE ─► [0550-DATEPARM-READ] ─► WS-DATEPARM-RECORD ─┐
TRANSACT-FILE ──► [1000-TRANFILE-GET-NEXT] ─► TRAN-RECORD ─────┤
XREF-FILE ──────► [1500-A-LOOKUP-XREF] ─► CARD-XREF-RECORD ────┤
TRANTYPE-FILE ──► [1500-B-LOOKUP-TRANTYPE] ─► TRAN-TYPE-RECORD ┼─► [1100/1120 report build] ─► FD-REPTFILE-REC ─► REPORT-FILE
TRANCATG-FILE ──► [1500-C-LOOKUP-TRANCATG] ─► TRAN-CAT-RECORD ─┘
(Per project diagram rules, no Mermaid/PlantUML markup is used; text-based diagrams render in all output media.)
The program issues exactly one CALL (per statements.by_verb.CALL = 1): CALL 'CEE3ABD' USING ABCODE, TIMING in 9999-ABEND-PROGRAM (L762, per spine XCORP_call_sites and metrics.static_vs_dynamic — static). CEE3ABD is the Language Environment callable service that forces an abnormal task termination; ABCODE is set to 999 and TIMING to 0 before the call (L760–L761). There are no calls to application subprograms (absence_factsno_app_calls) and no ON EXCEPTION/ON OVERFLOW handling on the CALL (absence_factsno_on_exception).
The program inlines 5 copybooks (per data_division.copybooks.length):
| Copybook | Provides | Consumers (blast radius) |
|---|---|---|
CVTRA05Y
|
TRAN-RECORD (350-byte transaction) |
9 consumers |
CVACT03Y
|
CARD-XREF-RECORD (50-byte card xref) |
12 consumers |
CVTRA03Y
|
TRAN-TYPE-RECORD (60-byte tran type) |
1 consumer |
CVTRA04Y
|
TRAN-CAT-RECORD (60-byte tran category) |
1 consumer |
CVTRA07Y
|
Report line records (name header, detail, totals) | 1 consumer |
Consumer counts are the exact values from spine COPYBOOK_CONSUMERS / cross_corpus.copybook_consumers. CVACT03Y (12 consumers) and CVTRA05Y (9 consumers) are the widely-shared shared-record copybooks; the remaining three are used only by CBTRN03C in this corpus.
Every file operation is followed by an explicit status check against '00', mapping the result into APPL-RESULT and then testing the 88-condition APPL-AOK. The pattern (open example, 0000-TRANFILE-OPEN, L511–L523): IF TRANFILE-STATUS = '00' → MOVE 0 TO APPL-RESULTELSEMOVE 12 TO APPL-RESULT; then IF APPL-AOK CONTINUE ELSE display an error, move the status to IO-STATUS, PERFORM 9910-DISPLAY-IO-STATUS, and PERFORM 9999-ABEND-PROGRAM. The two reads that can legitimately hit end-of-file (0550-DATEPARM-READ L354–L361 and 1000-TRANFILE-GET-NEXT L383–L390) use an EVALUATE on the status instead: WHEN '00' → 0, WHEN '10' → 16 (APPL-EOF, sets END-OF-FILE='Y' rather than abending), WHEN OTHER → 12 (abend). The three keyed lookups (1500-A/B/C) use READ ... INVALID KEY clauses that display the offending key, set IO-STATUS=23, and abend (L618–L642).
Per spine COND_SET_SITES, the two checked 88-conditions are activated by MOVEs into APPL-RESULT: APPL-AOK via MOVE 0 TO APPL-RESULT, APPL-EOF via MOVE 16 TO APPL-RESULT. Error disposition here is not uniform: most I/O errors route to 9999-ABEND-PROGRAM, but the end-of-file status ('10') on the two sequential reads is handled by setting the loop sentinel and continuing, not by abending.
9999-ABEND-PROGRAM (L758–L764) is the single hard-failure path: DISPLAY 'ABENDING PROGRAM', MOVE 0 TO TIMING, MOVE 999 TO ABCODE, CALL 'CEE3ABD' USING ABCODE, TIMING. It is invoked via PERFORM from 18 call sites (cite verbatim, per spine ABEND_CALL_SITES): the six OPENs, the six CLOSEs, the two sequential-read WHEN OTHER branches, the report-write error branch (1111-WRITE-REPORT-REC), and the three lookup INVALID KEY branches. On abend, the report GDG (TRANREPT(+1)) is deleted per its DISP=(...,DELETE).
9910-DISPLAY-IO-STATUS (L765–L781) formats the two-byte file status for display, handling both the non-numeric/'9'-prefixed VSAM-return case (via TWO-BYTES-BINARY/IO-STATUS-04) and the ordinary numeric case, emitting 'FILE STATUS IS: NNNN'.
The specific DISPLAY diagnostic strings preceding each abend (e.g. 'ERROR OPENING TRANFILE' at L519, 'ERROR READING TRANSACTION FILE' at L398, 'ERROR WRITING REPTFILE' at L486, 'INVALID CARD NUMBER : ' at L619) are present in source but are not carried in the unified STB's error_messages[] array (which is empty for this program) (source-derived — STB extraction gap: DISPLAY diagnostic text; not in unified STB).
The following I/O events are listed in source/harvest order (each at its verb's paragraph-definition line), not execution order — the I/O paragraphs are PERFORMed out of physical position (the OPENs run first at runtime though they sit physically late). Per spine IO_EVENTS (18 events):
READ DATE-PARMS-FILE INTO WS-DATEPARM-RECORD (L353) · READ TRANSACT-FILE INTO TRAN-RECORD (L381) · WRITE FD-REPTFILE-REC (L477) · OPEN TRANSACT-FILE (L510) · OPEN REPORT-FILE (L528) · OPEN XREF-FILE (L546) · OPEN TRANTYPE-FILE (L564) · OPEN TRANCATG-FILE (L582) · OPEN DATE-PARMS-FILE (L600) · READ XREF-FILE INTO CARD-XREF-RECORD (L617) · READ TRANTYPE-FILE INTO TRAN-TYPE-RECORD (L627) · READ TRANCATG-FILE INTO TRAN-CAT-RECORD (L637) · CLOSE TRANSACT-FILE (L648) · CLOSE REPORT-FILE (L666) · CLOSE XREF-FILE (L685) · CLOSE TRANTYPE-FILE (L703) · CLOSE TRANCATG-FILE (L721) · CLOSE DATE-PARMS-FILE (L739).
All six files are explicitly closed before GOBACK (per statements.by_verb.CLOSE = 6):
| File | Close paragraph | Status var checked |
|---|---|---|
TRANSACT-FILE
|
9000-TRANFILE-CLOSE (L648) |
TRANFILE-STATUS
|
REPORT-FILE
|
9100-REPTFILE-CLOSE (L666) |
TRANREPT-STATUS
|
XREF-FILE
|
9200-CARDXREF-CLOSE (L685) |
CARDXREF-STATUS
|
TRANTYPE-FILE
|
9300-TRANTYPE-CLOSE (L703) |
TRANTYPE-STATUS
|
TRANCATG-FILE
|
9400-TRANCATG-CLOSE (L721) |
TRANCATG-STATUS
|
DATE-PARMS-FILE
|
9500-DATEPARM-CLOSE (L739) |
DATEPARM-STATUS
|
Each close checks its status and abends on failure. The two sequential-file closes (9000, 9100) reset the result code via ADD 8 TO ZERO GIVING APPL-RESULT then SUBTRACT APPL-RESULT FROM APPL-RESULT on success (L647–L650, L665–L668); the four VSAM closes use plain MOVE.
| Field | Value |
|---|---|
| Program ID |
CBTRN03C (per identification_division.program_id, L23) |
| Source file |
CBTRN03C.CBL (per identification_division.header_comments.program) |
| Author |
AWS (L24) |
| Program kind / archetype | batch / batch_report (per program_kind, archetype) |
| Application | CardDemo (per header comment, L3) |
| Stated function | "Print the transaction detail report" (L5) |
| Program version banner |
* Ver: CardDemo_v2.0-25-gdb72e6b-235 Date: 2025-04-29 11:01:29 CDT (the program's own version stamp, L780, per spine VERSION_INFO) |
| Cyclomatic complexity | 44 (McCabe, per metrics.cyclomatic_complexity) |
| Perform nesting depth | 2 (per metrics.perform_nesting_depth) |
| Copybook-expanded source length | 781 lines (source-derived — STB extraction gap: source-counted line total of the copybook-expanded listing; not in unified STB) |
| Source computer / object computer | Not asserted — the program has no CONFIGURATION SECTION (per environment_division) |
| Generator | gold_plm_generation (v3 hybrid: L0 spine + unified STB) |
No SOURCE-COMPUTER / OBJECT-COMPUTER paragraphs are present in the program.
CBTRN03C is a reporting program; its "validation" is limited to date-window filtering and referential lookups, not field-level edit rules.
WS-START-DATE and WS-END-DATE are read from the DATEPARM file (L353) and define the inclusive reporting window. The in-program gate TRAN-PROC-TS (1:10) >= WS-START-DATE AND TRAN-PROC-TS (1:10) <= WS-END-DATE (L305–L306) compares the first 10 characters of the transaction processing timestamp (a (start:length) reference modification: characters 1 through 10, i.e. the date portion of the timestamp) against the window. As noted in s05, an out-of-range record drives NEXT SENTENCE (loop exit), and the upstream SORT already enforces the same range — so at runtime the gate is redundant with the SORT INCLUDE COND.XREF-FILE, TRANTYPE-FILE, and TRANCATG-FILE VSAM files respectively; a non-resolving key is treated as a hard error (INVALID KEY → abend, L618–L642), i.e. the report assumes referentially-complete master files.WS-FIRST-TIME (VALUE 'Y', L260) suppresses the account-total emission on the first transaction and triggers one-time header emission (L407–L412).There are no field-level edit/validation routines, no 88-level validation-flag families beyond the two I/O result conditions (APPL-AOK='0', APPL-EOF='16'), and no embedded SQL validation (absence_factsno_sql).
Amounts accumulate through three cross-record accumulators (per spine FIELD_ROLES — cross-record accumulators: WS-PAGE-TOTAL, WS-ACCOUNT-TOTAL, WS-GRAND-TOTAL, WS-LINE-COUNTER, APPL-RESULT):
1100-WRITE-TRANSACTION-REPORT does ADD TRAN-AMT TO WS-PAGE-TOTAL WS-ACCOUNT-TOTAL (L419).1120-WRITE-ACCOUNT-TOTALS emits WS-ACCOUNT-TOTAL and zeroes it (L439–L442).1110-WRITE-PAGE-TOTALS emits WS-PAGE-TOTAL, rolls it into WS-GRAND-TOTAL (ADD WS-PAGE-TOTAL TO WS-GRAND-TOTAL, L429), and zeroes the page total (L430).1110-WRITE-GRAND-TOTALS emits WS-GRAND-TOTAL (L451).
1100-WRITE-TRANSACTION-REPORT triggers a page break when FUNCTION MOD(WS-LINE-COUNTER, WS-PAGE-SIZE) = 0 (L414), where WS-PAGE-SIZE is 20 (L263–L264). On a page break it emits the page total and re-emits the column headers (1120-WRITE-HEADERS). WS-LINE-COUNTER is incremented on every report write (ADD 1 TO WS-LINE-COUNTER).
Two independent deterministic findings identify real defects in the total logic:
Confirmed accumulator double-write (L1_DETECTORS accumulator_double_writes, status CONFIRMED).
TRAN-AMT is added to WS-PAGE-TOTAL and WS-ACCOUNT-TOTAL at two sites: the per-record path (L419) and the EOF close-out path (L332). The source operand TRAN-AMT lives in TRAN-RECORD, whose only writer is a READ ... INTO (L381); because a READ INTO does not modify the receiving area on AT END, TRAN-AMT retains the final record's value at close-out, so the last transaction's amount is added a second time to the page and account totals (and thus into the grand total via the page roll-up). This is a genuine defect — the final page total, account total, and grand total are inflated by the last record's amount. It is not zeroed and not mutually exclusive with the per-record add.
Final-group (account) subtotal omission (CLOSEOUT_BRANCH_PERFORMS ⚠ FINAL-GROUP OMISSION).
1120-WRITE-ACCOUNT-TOTALS emits a card subtotal only on a control break (IF WS-CURR-CARD-NUM NOT= TRAN-CARD-NUM → performed at L315) and is absent from the EOF close-out branch (the else at L329 performs only 1110-WRITE-PAGE-TOTALS and 1110-WRITE-GRAND-TOTALS). Consequently the last card's account subtotal is never written — a dropped final subtotal, a latent defect. There is no last-card-break path and no EOF path that emits it; the omission should be documented as such rather than assumed handled.
Both findings are deterministic pipeline results (L1 detector + spine close-out resolution), presented here as verified rather than inferred.
CBTRN03C writes a 133-byte fixed report record (FD-REPTFILE-REC PIC X(133), L85). The report line images are defined in copybook CVTRA07Y (L175–L237):
| Report element | Record | Key fields / VALUEs |
|---|---|---|
| Report title / name header |
REPORT-NAME-HEADER
|
REPT-SHORT-NAME VALUE 'DALYREPT'; REPT-LONG-NAME VALUE 'Daily Transaction Report'; REPT-DATE-HEADER VALUE 'Date Range: '; REPT-START-DATE / REPT-END-DATE (populated from WS-START-DATE/WS-END-DATE at L409–L410) with ' to ' filler (L175–L184) |
| Column heading line |
TRANSACTION-HEADER-1
|
fixed captions: 'Transaction ID', 'Account ID', 'Transaction Type', 'Tran Category', 'Tran Source', 'Amount' (L204–L217) |
| Rule / separator line |
TRANSACTION-HEADER-2
|
PIC X(133) VALUE ALL '-' (L219) |
| Detail line |
TRANSACTION-DETAIL-REPORT
|
TRAN-REPORT-TRANS-ID, TRAN-REPORT-ACCOUNT-ID, TRAN-REPORT-TYPE-CD, TRAN-REPORT-TYPE-DESC, TRAN-REPORT-CAT-CD, TRAN-REPORT-CAT-DESC, TRAN-REPORT-SOURCE, TRAN-REPORT-AMT (PIC -ZZZ,ZZZ,ZZZ.ZZ) (L186–L202) |
| Page total |
REPORT-PAGE-TOTALS
|
caption 'Page Total' + dot leader; REPT-PAGE-TOTAL PIC +ZZZ,ZZZ,ZZZ.ZZ (L221–L225) |
| Account total |
REPORT-ACCOUNT-TOTALS
|
caption 'Account Total' + dot leader; REPT-ACCOUNT-TOTAL PIC +ZZZ,ZZZ,ZZZ.ZZ (L227–L231) |
| Grand total |
REPORT-GRAND-TOTALS
|
caption 'Grand Total' + dot leader; REPT-GRAND-TOTAL PIC +ZZZ,ZZZ,ZZZ.ZZ (L233–L237) |
WS-PAGE-SIZE = 20 (L263–L264); pagination via the MOD test at L414.WS-PAGE-TOTAL, WS-ACCOUNT-TOTAL, WS-GRAND-TOTAL (edited output counterparts REPT-PAGE-TOTAL, REPT-ACCOUNT-TOTAL, REPT-GRAND-TOTAL).TRAN-CARD-NUM vs WS-CURR-CARD-NUM (L313).WS-LINE-COUNTER is the sole line counter; there is no separate page-counter field (pagination is by the WS-LINE-COUNTER modulus, not a discrete page count).AFTER/BEFORE ADVANCING or AFTER PAGE clause (absence_factsno_carriage_control); page breaks are handled by re-emitting header lines, not by carriage control.Not applicable — the program performs no DB2/SQL access (absence_factsno_sql; sql empty) and no IMS DL/I calls (absence_factsno_ims_dli; ims_dli empty). All data access is VSAM/QSAM file I/O documented in s08.
Not applicable — batch program; no pseudo-conversational state machine (state_machines carries no kind: "state_machine" entry for this program).
Not applicable — batch program; no CICS COMMAREA (absence_facts: no CICS; program kind is batch).
Not applicable — batch program; no terminal interaction (pfkey_dispatch empty).
All copybooks are inlined in the DATA DIVISION (WORKING-STORAGE / FILE SECTION). Per data_division.copybooks:
| Copybook | Division / area | Provides | Consumers |
|---|---|---|---|
CVTRA05Y
|
WORKING-STORAGE |
TRAN-RECORD (350-byte transaction layout) |
9 |
CVACT03Y
|
WORKING-STORAGE |
CARD-XREF-RECORD (50-byte card xref) |
12 |
CVTRA03Y
|
WORKING-STORAGE |
TRAN-TYPE-RECORD (60-byte type record) |
1 |
CVTRA04Y
|
WORKING-STORAGE |
TRAN-CAT-RECORD (60-byte category record) |
1 |
CVTRA07Y
|
WORKING-STORAGE | Report line images | 1 |
Change-impact.
CVACT03Y has the largest blast radius at 12 consumers and CVTRA05Y at 9 consumers (exact counts per cross_corpus.copybook_consumers) — a change to either the card-xref or transaction record layout ripples across many CardDemo programs, so any modification must be coordinated corpus-wide. CVTRA03Y, CVTRA04Y, and CVTRA07Y are each used by only this program (1 consumer) in the corpus, so their blast radius is local.
Transcribed verbatim from spine JOB_FLOW (job TRANREPT, per jcl_facts):
| # | Step | Program |
|---|---|---|
| 1 | STEP05R.PRC001 | IDCAMS |
| 2 | STEP05R | SORT |
| 3 | STEP10R | CBTRN03C (this program) |
| Producer step.DD | Dataset | Consumer step.DD |
|---|---|---|
| STEP05R.PRC001 PRC001.FILEOUT | AWS.M2.CARDDEMO.TRANSACT.BKUP(+1) | STEP05R SORTIN |
| STEP05R SORTOUT | AWS.M2.CARDDEMO.TRANSACT.DALY(+1) | STEP10R TRANFILE |
| DD | Dataset | DISP | Direction |
|---|---|---|---|
| TRANFILE | AWS.M2.CARDDEMO.TRANSACT.DALY(+1) | SHR | input (fed by STEP05R SORTOUT) |
| CARDXREF | AWS.M2.CARDDEMO.CARDXREF.VSAM.KSDS | SHR | input |
| TRANTYPE | AWS.M2.CARDDEMO.TRANTYPE.VSAM.KSDS | SHR | input |
| TRANCATG | AWS.M2.CARDDEMO.TRANCATG.VSAM.KSDS | SHR | input |
| DATEPARM | AWS.M2.CARDDEMO.DATEPARM | SHR | input |
| TRANREPT | AWS.M2.CARDDEMO.TRANREPT(+1) | NEW,CATLG,DELETE | output |
cross_corpus.dataset_xref)AWS.M2.CARDDEMO.CARDXREF.VSAM.KSDS — read, recorded accessors: CBACT03C (read), CBACT04C (read), CBTRN02C (read), CBTRN03C (read) [accessor_count=4].AWS.M2.CARDDEMO.DATEPARM — read, sole recorded accessor (CBTRN03C) [accessor_count=1].AWS.M2.CARDDEMO.TRANCATG.VSAM.KSDS — read, sole recorded accessor (CBTRN03C) [accessor_count=1].AWS.M2.CARDDEMO.TRANREPT — write, sole recorded writer (CBTRN03C) [accessor_count=1].AWS.M2.CARDDEMO.TRANSACT.DALY — read, sole recorded accessor (CBTRN03C) [accessor_count=1].AWS.M2.CARDDEMO.TRANTYPE.VSAM.KSDS — read, sole recorded accessor (CBTRN03C) [accessor_count=1].These accessor counts are the recorded accessors in the corpus cross-reference, not a claim of corpus-exhaustiveness. The CARDXREF KSDS is the one dataset shared with other programs (four recorded readers, including this one).
Not applicable — CBTRN03C is a top-level batch report driver, not a called subprogram. It has no LINKAGE SECTION and returns to the system via GOBACK (L349); it passes no return code to a caller. The only outbound call is to the Language Environment abend service CEE3ABD (s07). Abnormal termination is signalled to the operating system via that abend (user code 999), not via a return-code contract.
Derived from the STB absence_facts[] array (12 entries, all status=absent, confidence=high):
no_sql).no_ims_dli).no_sort_merge) — sorting is done by the upstream JCL SORT step, not by CBTRN03C.no_app_calls) — the only CALL is to the LE service CEE3ABD.no_rewrite_delete, no_delete) — the program is read-only against its input masters and write-only to the report.no_string_inspect).no_compute) — arithmetic is ADD/SUBTRACT only.no_accept).no_goto).no_on_exception).no_carriage_control) — no AFTER/BEFORE ADVANCING or AFTER PAGE.End of PLM — CBTRN03C.