CBACT04C
Source: cbl/CBACT04C.cbl
Type: Batch program
CBACT04C — Interest Calculator (Batch)
Purpose
This batch COBOL program calculates monthly interest on credit card account balances, based on the transaction category balances accumulated for each account. For each account, it determines the applicable interest rate from a disclosure group table, computes monthly interest per transaction category, updates the account's running balance, and writes an interest-charge transaction record to a transaction file for downstream processing. It is executed as job step INTCALC.STEP15.
How it works
The main control flow (Procedure Division, starting line 180) does the following:
-
Startup: Opens all five files via
0000-TCATBALF-OPEN,0100-XREFFILE-OPEN,0200-DISCGRP-OPEN,0300-ACCTFILE-OPEN, and0400-TRANFILE-OPEN. Any file that fails to open (status ≠'00') causes an immediate abend. -
Main loop: Reads the transaction category balance file (
TCATBAL-FILE) sequentially via1000-TCATBALF-GET-NEXT, one record per iteration, until end-of-file. - When the account ID on the current record (
TRANCAT-ACCT-ID) differs from the previous one (WS-LAST-ACCT-NUM), the program treats this as a new account:- If not the first account processed, it calls
1050-UPDATE-ACCOUNTto post the accumulated interest (WS-TOTAL-INT) to the previous account's balance and reset cycle credit/debit totals to zero. - It resets the running interest total, then looks up the new account's master record (
1100-GET-ACCT-DATA) and its cross-reference record (1110-GET-XREF-DATA, keyed on account ID via the alternate key).
- If not the first account processed, it calls
- For every category-balance record (regardless of whether the account changed), it looks up the interest rate for that account's disclosure group / transaction type / category via
1200-GET-INTEREST-RATE.- If the specific group is not found (
DISCGRP-STATUS = '23'), it falls back to a hard-coded'DEFAULT'group ID and re-reads via1200-A-GET-DEFAULT-INT-RATE.
- If the specific group is not found (
- If a non-zero interest rate (
DIS-INT-RATE) is found, it computes interest (1300-COMPUTE-INTEREST) and calls a stubbed fee routine (1400-COMPUTE-FEES, currently a no-op — "To be implemented"). 1300-COMPUTE-INTERESTcomputes monthly interest as(TRAN-CAT-BAL * DIS-INT-RATE) / 1200, adds it to the account's running total, and calls1300-B-WRITE-TXto write a corresponding transaction record (type'01', category'05', source'System') to the output transaction file, including a generated DB2-style timestamp (Z-GET-DB2-FORMAT-TIMESTAMP).-
When the category-balance file reaches end-of-file, the loop performs one final
1050-UPDATE-ACCOUNTto post interest for the last account. -
Shutdown: Closes all five files (
9000–9400paragraphs), again abending on any close error, and displays a completion message.
Any file-status error during open, read, rewrite, write, or close triggers 9910-DISPLAY-IO-STATUS (formats and displays the numeric/status code) followed by 9999-ABEND-PROGRAM, which calls CEE3ABD with abend code 999 to terminate the job forcibly.
Inputs & outputs
| File / DD | Select name | Access | Purpose |
|---|---|---|---|
TCATBALF |
TCATBAL-FILE | Indexed, sequential read | Input driver file: transaction category balances per account (key: account ID + type code + category code). Drives the main processing loop. |
XREFFILE |
XREF-FILE | Indexed, random (by alternate key FD-XREF-ACCT-ID) |
Cross-reference lookup to get the card number associated with an account, used to populate the output transaction's card number. |
ACCTFILE |
ACCOUNT-FILE | Indexed, I-O (read and rewrite) | Account master file — read to get current balance data, rewritten to post accumulated interest and reset cycle credit/debit. |
DISCGRP |
DISCGRP-FILE | Indexed, random | Disclosure group table — provides the interest rate for a given account group / transaction type / category combination, with a fallback to a 'DEFAULT' group. |
TRANSACT |
TRANSACT-FILE | Sequential, output only | Output file — new interest-charge transaction records are appended here, one per computed interest amount. |
Copybooks CVACT01Y, CVACT03Y, CVTRA01Y, CVTRA02Y, CVTRA05Y define the working-storage record layouts (ACCOUNT-RECORD, CARD-XREF-RECORD, TRAN-CAT-BAL-RECORD, DIS-GROUP-RECORD, TRAN-RECORD) corresponding to these files. No CICS commands or SQL tables are used — this is a plain batch program. The program also accepts a linkage-section parameter PARM-DATE (10 bytes, from EXTERNAL-PARMS) used to build transaction IDs.
Things to know
- No SQL / CICS: Despite calculating interest for a "CardDemo" application, this program is pure batch COBOL with VSAM/indexed file I/O — no DB2 or CICS integration, though it generates a "DB2-format" timestamp string manually (
Z-GET-DB2-FORMAT-TIMESTAMP) rather than obtaining it from an actual DB2 call. - Hard-coded values: Transaction type code
'01', category code'05', and source'System'are hard-coded for every generated interest transaction. The fallback disclosure group ID'DEFAULT'is also hard-coded (line ~ in1200-GET-INTEREST-RATE). - Fee calculation stub:
1400-COMPUTE-FEESis empty ("To be implemented") — it is called but currently performs no work. Any downstream expectation of fee transactions will not be met by this version. - Error handling is uniform but harsh: virtually every I/O operation checks file status and calls
9999-ABEND-PROGRAMon any non-success status (except tolerated cases: end-of-file'10'on the driver file, and'23'/not-found on the disclosure group lookup). There is no retry or partial-recovery logic — any unexpected file status abends the whole job viaCALL 'CEE3ABD'with a fixed abend code of 999. - Transaction ID generation:
TRAN-IDis built by concatenatingPARM-DATEwith an in-memory counterWS-TRANID-SUFFIX(6 digits, incremented per transaction, not reset across runs within the program) — uniqueness depends entirely on the caller supplying a distinctPARM-DATEper run; there's no persistence of this counter across runs. - Account-boundary logic risk: The "new account" detection compares
TRANCAT-ACCT-IDtoWS-LAST-ACCT-NUMand assumes the category-balance file is sorted by account ID. If the input file is not properly sorted, interest postings could be applied to the wrong account or split incorrectly. - Interest skipped silently when rate is zero: If
DIS-INT-RATEis 0 for a category, no interest is computed or transaction written for that category — this is by design but could mask missing disclosure group configuration. 1100-GET-ACCT-DATA/1110-GET-XREF-DATA"not found" handling: These display a message onINVALID KEYbut do not abend immediately on invalid key alone — they only abend if the resulting file status is not'00'. Since a not-found on a random read is typically a non-'00'status, this effectively still leads to an abend, but the flow is a bit indirect (display message from theINVALID KEYclause, followed by the generic status check triggering the abend).- Fixed 3-decimal-position style constants: Interest is computed by dividing by
1200(12 months × 100 for percentage) — this assumesDIS-INT-RATEis stored as a whole-number percentage; misconfigured rate scaling would silently produce wrong interest amounts with no validation.
Files
| Logical file | DD name |
|---|---|
| TCATBAL-FILE | TCATBALF |
| XREF-FILE | XREFFILE |
| ACCOUNT-FILE | ACCTFILE |
| DISCGRP-FILE | DISCGRP |
| TRANSACT-FILE | TRANSACT |
Copybooks
CVACT01Y, CVACT03Y, CVTRA01Y, CVTRA02Y, CVTRA05Y
Calls
CEE3ABD
Executed by
INTCALC.STEP15
Paragraph flow
flowchart TD
0000_TCATBALF_OPEN["0000-TCATBALF-OPEN"]
0100_XREFFILE_OPEN["0100-XREFFILE-OPEN"]
0200_DISCGRP_OPEN["0200-DISCGRP-OPEN"]
0300_ACCTFILE_OPEN["0300-ACCTFILE-OPEN"]
0400_TRANFILE_OPEN["0400-TRANFILE-OPEN"]
1000_TCATBALF_GET_NEXT["1000-TCATBALF-GET-NEXT"]
1050_UPDATE_ACCOUNT["1050-UPDATE-ACCOUNT"]
1100_GET_ACCT_DATA["1100-GET-ACCT-DATA"]
1110_GET_XREF_DATA["1110-GET-XREF-DATA"]
1200_GET_INTEREST_RATE["1200-GET-INTEREST-RATE"]
1200_A_GET_DEFAULT_INT_RATE["1200-A-GET-DEFAULT-INT-RATE"]
1300_COMPUTE_INTEREST["1300-COMPUTE-INTEREST"]
1300_B_WRITE_TX["1300-B-WRITE-TX"]
1400_COMPUTE_FEES["1400-COMPUTE-FEES"]
9000_TCATBALF_CLOSE["9000-TCATBALF-CLOSE"]
9100_XREFFILE_CLOSE["9100-XREFFILE-CLOSE"]
9200_DISCGRP_CLOSE["9200-DISCGRP-CLOSE"]
9300_ACCTFILE_CLOSE["9300-ACCTFILE-CLOSE"]
9400_TRANFILE_CLOSE["9400-TRANFILE-CLOSE"]
Z_GET_DB2_FORMAT_TIMESTAMP["Z-GET-DB2-FORMAT-TIMESTAMP"]
9999_ABEND_PROGRAM["9999-ABEND-PROGRAM"]
9910_DISPLAY_IO_STATUS["9910-DISPLAY-IO-STATUS"]
0000_TCATBALF_OPEN --> 9910_DISPLAY_IO_STATUS
0000_TCATBALF_OPEN --> 9999_ABEND_PROGRAM
0100_XREFFILE_OPEN --> 9910_DISPLAY_IO_STATUS
0100_XREFFILE_OPEN --> 9999_ABEND_PROGRAM
0200_DISCGRP_OPEN --> 9910_DISPLAY_IO_STATUS
0200_DISCGRP_OPEN --> 9999_ABEND_PROGRAM
0300_ACCTFILE_OPEN --> 9910_DISPLAY_IO_STATUS
0300_ACCTFILE_OPEN --> 9999_ABEND_PROGRAM
0400_TRANFILE_OPEN --> 9910_DISPLAY_IO_STATUS
0400_TRANFILE_OPEN --> 9999_ABEND_PROGRAM
1000_TCATBALF_GET_NEXT --> 9910_DISPLAY_IO_STATUS
1000_TCATBALF_GET_NEXT --> 9999_ABEND_PROGRAM
1050_UPDATE_ACCOUNT --> 9910_DISPLAY_IO_STATUS
1050_UPDATE_ACCOUNT --> 9999_ABEND_PROGRAM
1100_GET_ACCT_DATA --> 9910_DISPLAY_IO_STATUS
1100_GET_ACCT_DATA --> 9999_ABEND_PROGRAM
1110_GET_XREF_DATA --> 9910_DISPLAY_IO_STATUS
1110_GET_XREF_DATA --> 9999_ABEND_PROGRAM
1200_A_GET_DEFAULT_INT_RATE --> 9910_DISPLAY_IO_STATUS
1200_A_GET_DEFAULT_INT_RATE --> 9999_ABEND_PROGRAM
1200_GET_INTEREST_RATE --> 1200_A_GET_DEFAULT_INT_RATE
1200_GET_INTEREST_RATE --> 9910_DISPLAY_IO_STATUS
1200_GET_INTEREST_RATE --> 9999_ABEND_PROGRAM
1300_B_WRITE_TX --> 9910_DISPLAY_IO_STATUS
1300_B_WRITE_TX --> 9999_ABEND_PROGRAM
1300_B_WRITE_TX --> Z_GET_DB2_FORMAT_TIMESTAMP
1300_COMPUTE_INTEREST --> 1300_B_WRITE_TX
9000_TCATBALF_CLOSE --> 9910_DISPLAY_IO_STATUS
9000_TCATBALF_CLOSE --> 9999_ABEND_PROGRAM
9100_XREFFILE_CLOSE --> 9910_DISPLAY_IO_STATUS
9100_XREFFILE_CLOSE --> 9999_ABEND_PROGRAM
9200_DISCGRP_CLOSE --> 9910_DISPLAY_IO_STATUS
9200_DISCGRP_CLOSE --> 9999_ABEND_PROGRAM
9300_ACCTFILE_CLOSE --> 9910_DISPLAY_IO_STATUS
9300_ACCTFILE_CLOSE --> 9999_ABEND_PROGRAM
9400_TRANFILE_CLOSE --> 9910_DISPLAY_IO_STATUS
9400_TRANFILE_CLOSE --> 9999_ABEND_PROGRAM
Paragraphs
| Paragraph | Line | Performs |
|---|---|---|
| 0000-TCATBALF-OPEN | 234 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 0100-XREFFILE-OPEN | 252 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 0200-DISCGRP-OPEN | 270 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 0300-ACCTFILE-OPEN | 289 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 0400-TRANFILE-OPEN | 307 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 1000-TCATBALF-GET-NEXT | 325 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 1050-UPDATE-ACCOUNT | 350 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 1100-GET-ACCT-DATA | 372 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 1110-GET-XREF-DATA | 393 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 1200-GET-INTEREST-RATE | 415 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM, 1200-A-GET-DEFAULT-INT-RATE |
| 1200-A-GET-DEFAULT-INT-RATE | 443 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 1300-COMPUTE-INTEREST | 462 | 1300-B-WRITE-TX |
| 1300-B-WRITE-TX | 473 | Z-GET-DB2-FORMAT-TIMESTAMP, 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 1400-COMPUTE-FEES | 518 | |
| 9000-TCATBALF-CLOSE | 522 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 9100-XREFFILE-CLOSE | 541 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 9200-DISCGRP-CLOSE | 559 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 9300-ACCTFILE-CLOSE | 577 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| 9400-TRANFILE-CLOSE | 595 | 9910-DISPLAY-IO-STATUS, 9999-ABEND-PROGRAM |
| Z-GET-DB2-FORMAT-TIMESTAMP | 613 | |
| 9999-ABEND-PROGRAM | 628 | |
| 9910-DISPLAY-IO-STATUS | 635 |