CARDCOR/i — Credit Card Core

SteelFrame X application operation manual  ·  ← back to Operation Manuals  ·  Sign On

CARDCOR/i is a bank card-issuing back office: cardholder and account masters, card issuance, real-time authorization against open-to-buy, settlement of authorized purchases into a posted ledger, average-daily-balance finance charges, delinquency and credit-limit management, a statement/minimum-payment cycle with a printed spool, payments, a balanced general-ledger feed, and a full dispute/chargeback lifecycle. It is a mixed-language IBM i application — fixed- and free-form RPG, ILE COBOL, CL, DDS (PF/LF, display, printer) and embedded/DDL SQL — running in library CARDCOR. This manual is the reference for the operator who runs the online screens and the night-batch chain, and for the developer maintaining it. It is grounded entirely in the committed source (creditcard-app/src/sources.mjs, src/seed.mjs, the test/cc_*.mjs drivers and PLAN.md), and it is honest where the application deliberately simplifies or where a documented gap exists.

Contents

A. Overview & Architecture ↑ top

A.1 What it does

CARDCOR/i services the life of a revolving credit-card account:

A.2 Native-record + SQL architecture

Unlike the SQL-PL-centred sibling apps, CARDCOR/i keeps its business logic in the RPG/COBOL programs and uses DB2 for i primarily as a keyed record store, reached through native record I/O (CHAIN / READ / WRITE / UPDATE / SETLL over DDS PFs and LFs). SQL appears in two deliberate places:

The application follows the proven POLARIS/i house style (the column-exact RPG C()/D() helpers in sources.mjs) and is coded around several documented emulator quirks — nested if/else only (free-form elseif mis-scopes its END-IF), packed id parameters to match packed callers, UF A opens where a program both CHAINs and WRITEs the same file, and PRTF field columns whose position digits end at DDS column 44. Everything runs in library CARDCOR.

A.3 Component & flow

  ONLINE (5250)          FRONT-OFFICE            NIGHT BATCH (CCNIGHT -> CCNITEQ *JOBQ)
  -------------          ------------            -------------------------------------
  CCMENUP  --CALL-->     CCISSUE  (issue card)   CCPOST  --settle AUTHTRN 'A' -> POSTTRN 'P'
    opt 1 -> CCACCINQ    CCAUTH   (authorize)             (+ACCBAL, release HELDAUTH, A->S)
    opt 2 -> CCTRNINQ      OTB check -> AUTHTRN   CCFIN   --ADB finance charge -> POSTTRN 'I'
             (SFL over      'A'/'D'               CCDELQ  --late/over-limit fee -> POSTTRN 'F'
              POSTLF)     CCPAY    (payment)      CCGL    --POSTTRN -> balanced GLENTRY DR/CR
                            -> POSTTRN 'Y'                 (EXEC SQL, COMMIT)
                          CCDISP   (open disp.)   CCSTMT  --cut cycle -> STMTHDR + PRTF spool
                            -> DISPTRN 'O'
                            + POSTTRN 'C'          CCACCRPT (COBOL) -- portfolio report,
                          CCCHGBK  (resolve)                GLENTRY cross-check "IN BALANCE"
                            'W' stand / 'L' rebill
                            -> POSTTRN 'B'
              \                                   /
               \                                 /
                v                               v
     ACCTMST (account master: ACCBAL, HELDAUTH, DELQBKT, ASTAT, MINPAY, LASTBAL)
        |   \        \                    journaled ledgers: AUTHTRN, POSTTRN, DISPTRN
        |    +--> AUTHTRN (auth log)              (CCJRN, IMAGES(*BOTH))
        |    +--> POSTTRN (posted ledger) --POSTLF--> CCTRNINQ subfile
        |    +--> DISPTRN (dispute ledger) --DISPLF--> per-account dispute history
        +------> STMTHDR (statement header)  +  STMTP spool  +  GLENTRY (GL feed)

A single event — say a purchase — flows: CCAUTH approves it and writes an AUTHTRN 'A' row plus a HELDAUTH hold → the night CCPOST settles it into a POSTTRN 'P' row, adds the amount to ACCBAL, releases the hold and flips the auth marker 'A'→'S' so a re-run skips it → CCGL later mirrors that posted row into a balanced GLENTRY DR/CR pair.

A.4 Object inventory

ObjectTypeRole
CARDHLDRPFCardholder master (party data).
ACCTMSTPFAccount master (the heart of the app).
CARDMSTPFIssued plastics (one+ per account).
CARDLFLFCards keyed by account then card.
AUTHTRNPF (journaled)Authorization log (arrival AUTHID).
POSTTRNPF (journaled)Posted-transaction ledger (arrival POSTID).
POSTLFLFPosted txns by account (subfile source).
FEESCHEDPFFee/rate schedule (MIN/LTE/OVL).
STMTHDRPFStatement/cycle header.
DELQLFLFDelinquency-bucket management view over ACCTMST.
DISPTRNPF (journaled)Dispute/chargeback ledger (arrival DISPID).
DISPLFLFDisputes by account then dispute id.
GLENTRYSQL tableBalanced GL DR/CR feed.
ACCTDSPFDSPFAccount-inquiry display file.
TRNDSPFDSPF (SFL)Transaction-inquiry subfile display file.
CCMENUDDSPFOperator-menu display file.
STMTPPRTFStatement printer file (spool).
CCISSUE / CCAUTH / CCPOST / CCPAYRPGLEIssue, authorize, settle, pay.
CCFIN / CCDELQ / CCSTMT / CCGLRPGLEFinance charge, delinquency, statement, GL.
CCDISP / CCCHGBKRPGLEOpen / resolve a dispute.
CCACCMNTRPGLEAccount-master maintenance (A/C/I/X).
CCACCINQ / CCTRNINQ / CCMENUPRPGLE5250 inquiries + menu.
CCACCRPTCBLLECOBOL portfolio report + GL cross-check.
CCSETUPCLPSingle build entry point (objects + journal + seed).
CCNIGHTCLPNight-batch SBMJOB chain onto CCNITEQ.

The full catalogue is 8 PFs + 3 LFs + 1 SQL table, 3 DSPF + 1 PRTF, 15 RPG programs, 1 COBOL program and 2 CL programs, with three ledger tables journaled to CCJRN. Sections D and F expand each.

B. Online Transactions & Screens ↑ top

B.1 The command/entry line

CARDCOR/i has no CICS transaction identifiers and no transid switch. On IBM i, each program is reached by name from a 5250 command-entry line (or through the operator menu, or a JOBQ/scheduler for the batch chain). The operator equivalent of “type a transid and Enter” is “type a CALL command and Enter”. Before invoking anything, the job's library list must include CARDCOR — the tested jobs run with LIBL = QSYS QGPL CARDCOR QTEMP and CURLIB = CARDCOR.

To do thisType on the command line
Open the operator menuCALL CARDCOR/CCMENUP
Account inquiry (direct)CALL CARDCOR/CCACCINQ
Transaction inquiry (subfile, direct)CALL CARDCOR/CCTRNINQ
Issue a card onto an open accountCALL CARDCOR/CCISSUE (PARM acct, card, expiry)
Authorize a purchaseCALL CARDCOR/CCAUTH (PARM acct, card, date, amt, MCC, rsp)
Apply a paymentCALL CARDCOR/CCPAY (PARM acct, amt)
Account-master maintenanceCALL CARDCOR/CCACCMNT (PARM action + fields)
Open / resolve a disputeCALL CARDCOR/CCDISP / CALL CARDCOR/CCCHGBK
Run the whole night batchCALL CARDCOR/CCNIGHT (or SBMJOB it)

The front-office programs (CCISSUE, CCAUTH, CCPAY, CCACCMNT, CCDISP, CCCHGBK) are callable with an *ENTRY PLIST — they take parameters and return a response code, so in practice they are invoked program-to-program (an acquiring feed, a teller front-end, or a test driver) rather than typed by hand. The three interactive programs (CCMENUP, CCACCINQ, CCTRNINQ) are 5250 screens. The batch programs are described in section C.

B.2 The menu & inquiry screens

CARDCOR/i has three real 5250 programs. CCMENUP (over DDS CCMENUD) is the operator menu; it routes option 1 to CCACCINQ and option 2 to CCTRNINQ with a classic indicator-conditioned dynamic CALL, rejects any other option with “Invalid option”, and exits on F3.

Account inquiry — CCACCINQ / ACCTDSPF

A plain (non-subfile) EXFMT loop over record format ACCINQF. The operator keys an account number into IACCTNO and presses Enter; the program CHAINs ACCTMST, then CHAINs CARDHLDR for the holder name, and renders status, credit limit, balance, open-to-buy (CRLIMIT − ACCBAL − HELDAUTH), APR, minimum payment and delinquency bucket. A not-found account clears the output fields and shows “Account not found: <acct>”. F3 exits.

Account Inquiry - CARDCOR/i Account number: AC00000001 Holder . . . . : AVERY NORTHWOOD Status . . . . : O Credit limit . : 5000.00 Balance . . . : 200.00 Open to buy . : 4800.00 APR (pct). . . : 18.00 Min pay due . : 0.00 Delinq bucket : 0 Account found. F3=Exit Enter=Inquire

Transaction inquiry — CCTRNINQ / TRNDSPF (subfile)

A header record plus a real SFL/SFLCTL subfile (TRNSFL under TRNCTL, SFLPAG(0010) per page, SFLSIZ(0020)). The operator keys an account number; the program clears the subfile (SFLCLR), then loads it from POSTLF (the posted-ledger logical keyed by account) with SETLL/READ, writing one TRNSFL row per posted transaction until the account changes, and displays it. The SFL indicators mirror the DDS numbering: 31=SFLDSP, 32=SFLDSPCTL, 33=SFLCLR, 34=SFLEND(*MORE); SFLDSPCTL must be on before the first EXFMT or nothing renders.

Transaction Inquiry - CARDCOR/i Account number: AC00000001 Balance . . . : 500.00 Status . . : O PostID Date Ty Amount Description 1 20260201 P 300.00 PURCHASE SETTLEMENT 2 20260201 Y -100.00 PAYMENT RECEIVED 3 20260201 I 10.95 FINANCE CHARGE Account found. F3=Exit Enter=Inquire
The column-header row (PostID / Date / Ty / Amount / Description) sits on DDS row 6, one line above the subfile body on row 7. An earlier revision authored the headers on the same row 7 as the first data fields, colliding the 1-char STRNTYP under the 2-char 'Ty' literal and rendering Yy/Py/Iy in the Ty column; that DDS row/column defect was found and corrected (SIM-2026-Q6). The Ty column now shows a clean single Y/P/I. Negative amounts (payments) render with a correct leading minus, because the amount is a plain 14A alphanumeric field populated by RPG %char() (which carries the sign), not a numeric EDTCDE field.

B.3 Controls & audit workflow (no four-eyes maker–checker)

Honest statement: CARDCOR/i does not model a four-eyes maker–checker / separate-authorization workflow. There is no “one user requests, a second user approves” step: a CCAUTH approves-or-declines in one call, CCPAY and CCDISP act immediately, and account maintenance through CCACCMNT applies at once. The manual documents the control model the application does have:

In sum, the control posture is immutable journaled ledgers + status/state gating + duplicate-add guarding, enforced in the application programs, rather than a segregation-of-duties approval workflow.

C. Batch Jobs & the Periodic Cycle ↑ top

CARDCOR/i's servicing runs as a night-batch chain packaged by the CL program CCNIGHT, which creates the CCNITEQ job queue and submits each step as its own SBMJOB in sequence: CCPOST → CCFIN → CCDELQ → CCGL → CCSTMT. Each step is a separate job on purpose — a documented platform quirk means rows written to an output-opened file may not be visible to a program CALLed inline in the same job until the writer closes, so the steps are not chained as one CL calling all five inline. (The test harness drives the individual steps directly where it must assert between them; CCNIGHT is the packaged operator entry point that proves the chain exists as one command.)

-- run the whole night chain (creates CCNITEQ, submits the five steps)
CALL PGM(CARDCOR/CCNIGHT)

-- or submit an individual step
SBMJOB CMD(CALL PGM(CARDCOR/CCPOST)) JOB(CCPOSTJ) JOBQ(CARDCOR/CCNITEQ)
SBMJOB CMD(CALL PGM(CARDCOR/CCSTMT) PARM('001')) JOB(CCSTMTJ) JOBQ(CARDCOR/CCNITEQ)

Most batch programs take no CALL parameters and read the whole account or ledger file forward; only CCSTMT takes a parameter (the 3-digit cycle number). None of these programs reads a control-date row — the posting date on the ledger rows they write is a fixed literal (20260201) in the current source (see F.5), so the “processing date” is compiled-in rather than operator-supplied.

C.1 Full batch program set

ProgramPurposeReads / writesInputsOutputs (DSPLY)Frequency
CCPOST Settle each approved-unsettled authorization into the ledger + balance. Reads AUTHTRN (arrival); writes POSTTRN 'P'; UPDATEs ACCTMST (+ACCBAL, −HELDAUTH floored 0); flips AUTHRSP 'A'→'S'. None. CCPOST SETTLED=n SKIP=n. Nightly.
CCFIN Assess an average-daily-balance finance charge on every open, non-zero account. Reads/UPDATEs ACCTMST; writes POSTTRN 'I'. None (ADB proxy = (LASTBAL+ACCBAL)/2; rate = APR/1200). CCFIN CHARGED=n SKIP=n TOTFC=amt. Monthly (per cycle).
CCDELQ Over-limit + late-fee assessment over the delinquency LF, with a freeze path. Reads DELQLF; reads FEESCHED; writes POSTTRN 'F'; UPDATEs ACCTMST (+fee; ASTAT→'F' if DELQBKT≥3). None (fees from FEESCHED OVL/LTE, defaults 35.00/29.00). CCDELQ LATE=n OVL=n FROZEN=n. Monthly (per cycle).
CCGL Consolidate the posted ledger into balanced GL DR/CR pairs (embedded SQL, commitment control). Reads POSTTRN (arrival); inserts two GLENTRY rows per posted row; EXEC SQL COMMIT. None (BATCHID literal 1). CCGL BATCH=1 ROWS=n. Nightly / per cycle.
CCSTMT Cut the statement cycle: new balance, minimum payment, STMTHDR row + PRTF spool. Reads/UPDATEs ACCTMST (MINPAY, LASTBAL=NEWBAL); reads FEESCHED; writes STMTHDR; prints STMTP. Cycle number (3-digit *ENTRY PLIST). CCSTMT CUT=n CYCLE=nnn; spooled statement. Monthly (cycle cut).
CCACCRPT COBOL portfolio report: total open-account balance, cross-check vs GLENTRY receivable DR. Reads ACCTMST (COMP-3 FD); EXEC SQL SUM over GLENTRY. None. CCACCRPT OPEN-ACCTS n BAL amt / NONOPEN-ACCTS n / GLENTRY-RECV-DR n AMT amt. After CCGL (reconcile).

Front-office CCISSUE, CCAUTH, CCPAY, CCACCMNT, CCDISP and CCCHGBK are on-demand callables, not part of the night chain; they are driven as transactions arrive.

C.2 Auth / posting / statement / finance detail

Authorization — CCAUTH (front-office)

Computes open-to-buy = CRLIMIT − ACCBAL − HELDAUTH with packed-decimal math. A blocked/non-open account (ASTAT≠'O') is declined. Otherwise if AUTHAMT ≤ OTB the auth is approved ('A'): HELDAUTH rises by the amount (the hold) and an AUTHTRN 'A' row is written; else declined ('D'), no hold, but an AUTHTRN 'D' row is still logged. The response code is returned in the last PARM.

Seeded example (from cc_daycycle):
  AC00000001 bal 200.00, limit 5000.00, held 0  -> OTB 4800.00
  CCAUTH 300.00 -> A  (300 <= 4800)      HELDAUTH now 300.00
  AC00000002 bal 4800.00, limit 5000.00          -> OTB 200.00
  CCAUTH 200.00 -> A  (fits exactly)
  CCAUTH 500.00 -> D  (OTB now 0, declined)

Posting / settlement — CCPOST

One forward pass over AUTHTRN in arrival sequence. For each auth still marked 'A' (approved-unsettled) it writes a POSTTRN 'P' (PURCHASE SETTLEMENT) row, adds AUTHAMT to ACCTMST.ACCBAL, releases the hold (HELDAUTH −= AUTHAMT, floored at 0), and flips the auth marker 'A'→'S' in place so a re-run is idempotent (already-settled 'S'/declined 'D' auths are skipped). It seeds the next POSTID once from COALESCE(MAX(POSTID),0) via embedded SQL, then bumps a local counter per WRITE.

Expected DSPLY:
  CCPOST SETTLED=2 SKIP=1   2 approved auths settled, 1 declined skipped

Finance charge — CCFIN

Forward pass over ACCTMST. For every account with ASTAT='O' and ACCBAL > 0: ADB = (LASTBAL + ACCBAL) / 2 (a two-point average-daily-balance proxy — a real system tracks per-day balances; documented in PLAN.md), monthly periodic rate = APR / 1200 (APR is an annual percent, so /100 for a fraction and /12 for the month), finance charge = %DECH(ADB × rate). A positive charge is written as a POSTTRN 'I' (FINANCE CHARGE) row and added to ACCBAL; zero-balance accounts are skipped.

Worked example (SIM-2026-M3, AC1 at month-accumulated 730.00):
  ADB 730.00 x (18.00/1200) = 730.00 x 0.015 = 10.95   -> POSTTRN 'I' 10.95

Delinquency / credit-limit — CCDELQ

Forward pass over DELQLF (keyed DELQBKT then ACCTNO, so it reads worst-first within a bucket). Per account: if ACCBAL > CRLIMIT it assesses an over-limit fee (FEESCHED 'OVL', default 35.00); else if DELQBKT ≥ 1 (past due) it assesses a late fee (FEESCHED 'LTE', default 29.00). Either fee is written as a POSTTRN 'F' (DELINQUENCY FEE) row and added to ACCBAL; if DELQBKT ≥ 3 the account is frozen (ASTAT→'F').

Honest gap (SIM-2026-Y4): CCDELQ's header comment says it “bumps DELQBKT”, but the code never actually increments DELQBKT anywhere — it writes the fee row and grows the balance only. Because the freeze path requires DELQBKT ≥ 3 and nothing in CCDELQ advances the bucket, an account can be frozen only if it is seeded at bucket 3 (as the day-cycle test's AC00000003 is) or its balance exceeds the limit; a chronically past-due account at bucket 1 will be late-fee'd every cycle but is structurally never auto-frozen by CCDELQ alone. This is a confirmed comment/implementation mismatch, not a platform bug.

Statement / minimum payment — CCSTMT

Given a cycle number, a forward pass over ACCTMST cuts a statement for every open, non-zero account: NEWBAL = ACCBAL; minimum payment = the greater of a floor (FEESCHED 'MIN' FEEAMT, default 25.00) or a percentage of the balance (FEESCHED 'MIN' FEEFCTR, default 0.020 = 2%), never more than the whole balance. It writes a STMTHDR row (keyed SACCTNO+CYCLENO), records MINPAY on the account, snapshots LASTBAL = NEWBAL (setting up the next cycle's ADB start point), and prints a real STMTP statement line to the spool.

Example (cc_daycycle, AC2 at 5125.00): 2% x 5125.00 = 102.50 > 25.00 floor
  MINPAY = 102.50    STMTHDR written    LASTBAL <- 5125.00
  CCSTMT CUT=n CYCLE=001

General-ledger feed — CCGL

One pass over POSTTRN in arrival sequence, mapping each posted row to a balanced DR/CR pair (chart-of-accounts-lite, documented, not a real GL): P DR 1400-RECV / CR 2200-STLMT; Y DR 1000-CASH / CR 1400-RECV; F DR 1400-RECV / CR 4200-FEEIN; I DR 1400-RECV / CR 4100-FININ. TRNAMT is signed; each posting uses %ABS so DR and CR magnitudes match, keeping SUM(DR) = SUM(CR) by construction. Both inserts run as embedded EXEC SQL under the job's commitment definition, finalised with a real EXEC SQL COMMIT.

Honest note (SIM-2026-M3/Y4): CCGL carries no posting watermark, so it re-posts the whole POSTTRN table every run. After a single run the row count is exactly 2 × the posted-row count; run across multiple cycles the GLENTRY count inflates past that (same non-idempotent-batch family as the sibling apps' GL posters). It stays balanced (SUM(DR)=SUM(CR)) every time; operators run it once per cycle by discipline rather than relying on a re-run guard.

C.3 Ordering & dependencies

D. Data Files (data dictionary) ↑ top

All files are in library CARDCOR, grounded in src/sources.mjs. Money is packed decimal 11P2 (nine integer digits) or 9P2; dates are stored as zoned integers in YYYYMMDD (or YYYYMM forms as noted); APR is packed 5P2 as an annual percent (18.00 = 18%); the fee factor is packed 5P3 (0.020 = 2%).

CARDHLDR — Cardholder master (UNIQUE, key CHNO)

FieldTypeMeaning
CHNO7ACardholder number (key).
HNAME25AHolder name.
ADDR / CITY / ST / ZIP25A / 15A / 2A / 5AMailing address.
DOB8S 0Date of birth (YYYYMMDD).

ACCTMST — Account master (UNIQUE, key ACCTNO)

FieldTypeMeaning
ACCTNO10AAccount number (key), e.g. AC00000001.
CHNO7AOwning cardholder.
CRLIMIT11P 2Credit limit.
CASHLIM11P 2Cash-advance limit.
ACCBAL11P 2Current balance (the running revolving balance).
HELDAUTH11P 2Sum of open (unsettled) authorization holds.
APR5P 2Annual percentage rate (percent; 18.00 = 18%).
CYCDAY2S 0Statement cycle day-of-month.
ASTAT1AStatus: O open, F frozen, C closed.
DELQBKT1S 0Delinquency bucket (0–n; drives fees/freeze). See F.5 note.
LASTBAL11P 2Last statement balance (ADB start point).
MINPAY9P 2Minimum payment due (set by CCSTMT).

CARDMST — Issued cards (UNIQUE, key CARDNO); CARDLF — LF by ACCTNO+CARDNO

FieldTypeMeaning
CARDNO16A16-digit card number (key).
ACCTNO10AOwning account.
EXPDT6S 0Expiry (YYYYMM).
CSTAT1ACard status: A active (written by CCISSUE); B blocked.

CARDLF is a logical over CARDMST keyed ACCTNO then CARDNO — an account's cards in order.

AUTHTRN — Authorization log (UNIQUE arrival, key AUTHID) — journaled

FieldTypeMeaning
AUTHID8S 0Arrival-sequence auth id (key).
ACCTNO / CARDNO10A / 16AAccount and card authorized.
AUTHDT8S 0Authorization date (YYYYMMDD).
AUTHAMT11P 2Authorized amount.
AUTHRSP1AResponse: A approved, D declined; CCPOST flips A→S when settled.
MCC4AMerchant category code.

POSTTRN — Posted-transaction ledger (UNIQUE arrival, key POSTID) — journaled

FieldTypeMeaning
POSTID8S 0Arrival-sequence posted id (key).
ACCTNO10AAccount.
POSTDT8S 0Post date (YYYYMMDD; literal 20260201 in current source — see F.5).
TRNTYPE1AP purchase, Y payment, F fee, I interest, C dispute provisional credit, B rebill. See F.4.
TRNAMT11P 2Signed amount (payments/credits negative).
DESCR20AFree-text detail (e.g. PURCHASE SETTLEMENT).

POSTLF is a logical over POSTTRN keyed ACCTNO then POSTID — the source the CCTRNINQ subfile loads from.

FEESCHED — Fee/rate schedule (UNIQUE, key FEECD)

FieldTypeMeaning
FEECD3AFee code (key): MIN, LTE, OVL.
FEEAMT9P 2Flat amount / floor.
FEEFCTR5P 3Rate factor (e.g. 0.020 = 2% for MIN).
FDESC20ADescription.

Seeded rows (from the day-cycle driver): MIN 25.00 / 0.020 (min-payment floor/pct), LTE 29.00 (late fee), OVL 35.00 (over-limit fee). Programs fall back to hard-coded defaults (25.00/0.020, 29.00, 35.00) if a code is missing.

STMTHDR — Statement/cycle header (UNIQUE, key SACCTNO+CYCLENO)

FieldTypeMeaning
SACCTNO / CYCLENO10A / 3S 0Account + cycle number (key).
STMTDT8S 0Statement date.
NEWBAL11P 2New balance billed.
MINDUE9P 2Minimum payment due.
SDUEDT8S 0Payment due date.
FINCHG9P 2Finance charge shown on the statement (0 in current CCSTMT).

DELQLF — Delinquency-management LF over ACCTMST (key DELQBKT+ACCTNO)

A logical over ACCTMST exposing ACCTNO, CHNO, CRLIMIT, ACCBAL, APR, ASTAT, DELQBKT, MINPAY, keyed DELQBKT then ACCTNO so a delinquency pass reads worst-first-in-bucket order. CCDELQ scans this LF but CHAINs the base ACCTMST for its UPDATE.

DISPTRN — Dispute/chargeback ledger (UNIQUE arrival, key DISPID) — journaled

FieldTypeMeaning
DISPID8S 0Arrival-sequence dispute id (key).
ACCTNO10ADisputing account.
DPOSTID8S 0The disputed POSTTRN.POSTID (a purchase).
DISPDT8S 0Dispute date.
DISPAMT11P 2Disputed amount.
DREASON4ADispute reason code.
DSTAT1ALifecycle: O open (provisional credit issued), W won (credit permanent), L lost (credit reversed/rebilled).

DISPLF is a logical over DISPTRN keyed ACCTNO then DISPID — an account's dispute history in order.

GLENTRY — GL posting feed (SQL DDL table)

ColumnTypeMeaning
BATCHIDINTEGERGL batch id (literal 1 from CCGL).
ACCTCHAR(10)GL account, e.g. 1400-RECV, 2200-STLMT.
DRCRCHAR(1)D debit / C credit.
AMTDECIMAL(11,2)Posting magnitude (always positive; %ABS applied).
SRCPGMCHAR(10)Source program (CCGL).

Relationships

E. Operations Runbook ↑ top

E.1 Day-in-the-life

  1. Build / verify the library (first time or after a reset): create CARDCOR, load the source with the seeder, compile CCSETUP and run it — CALL PGM(CARDCOR/CCSETUP) creates every object, journals the three ledger tables to CCJRN, and is the single build entry point.
  2. Set up masters as needed: CALL CARDCOR/CCACCMNT (add / change / close accounts) and CALL CARDCOR/CCISSUE (issue a card onto an open account).
  3. Take authorizations through the day: each CALL CARDCOR/CCAUTH approves or declines against open-to-buy and logs an AUTHTRN row; approvals place a HELDAUTH hold.
  4. Take payments as they arrive: CALL CARDCOR/CCPAY (account + amount) reduces the balance and writes a POSTTRN 'Y' row.
  5. Handle disputes on demand: CALL CARDCOR/CCDISP opens a dispute (provisional credit), CALL CARDCOR/CCCHGBK resolves it won/lost.
  6. Run the night batch: CALL CARDCOR/CCNIGHT queues CCPOST → CCFIN → CCDELQ → CCGL → CCSTMT onto CCNITEQ.
  7. Operators can inquire any time via CALL CARDCOR/CCMENUP (account / transaction inquiry).

Pre-checks: confirm the job's library list includes CARDCOR; confirm the three ledger tables are journaled to CCJRN (they are, after CCSETUP).

Post-checks after posting (CCPOST):

E.2 Statement / month-end close

  1. Confirm the day's/cycle's authorizations are posted (CCPOST) so balances are current.
  2. Run the finance charge and delinquency passes: CCFIN then CCDELQ (order matters — both feed the balance the statement bills).
  3. Post the GL feed: CCGL (once per cycle — no re-run guard; see C.2 note).
  4. Cut the statement: CALL CARDCOR/CCSTMT PARM('001') (the cycle number). Confirm CCSTMT CUT=n CYCLE=001 and inspect the spooled statement.
  5. Reconcile with the COBOL report: CALL CARDCOR/CCACCRPT.

Reconciling figures (the same the day-cycle and SIM drivers assert):

E.3 Failure & re-run rules

Each program DSPLYs a one-line summary. The safe-to-rerun posture differs per program:

SituationBehaviourAction
Re-run CCPOSTAlready-settled auths are 'S' and skipped.Idempotent — safe to re-submit; only genuinely-approved-unsettled auths settle.
Re-run CCISSUE for an existing cardIdempotent CHAIN check skips the write.Safe no-op (CCISSUE SKIP: CARD EXISTS).
Re-run CCGLNot idempotent — re-posts the whole ledger; GLENTRY count inflates.Run once per cycle by discipline. GL stays balanced but rows duplicate; do not re-run to “top up”.
Re-run CCFIN / CCDELQ / CCSTMTNo period watermark — each re-run re-charges / re-fees / re-cuts.Run each exactly once per cycle. CCSTMT is keyed SACCTNO+CYCLENO, so re-cutting the same cycle number is blocked by the unique key; a new cycle number cuts again.
CCAUTH against a frozen/closed accountDeclined (ASTAT≠'O').Expected; an AUTHTRN 'D' row is still logged for the trail.
CCDISP rejectedAccount missing, POSTID missing, or the posted txn is not a purchase ('P').POUTRSP='R', no rows written; correct the input and retry.
CCCHGBK rejectedDispute missing, not still open, or bad outcome (not W/L).POUTRSP='R'; only an open dispute can be resolved once.
Duplicate account add (CCACCMNT 'A')Guarding CHAIN detects it; the existing row is not overwritten.RSP=R; the balance/limit are unchanged (verified by cc_build).
Because every money movement is journaled to POSTTRN (and auths to AUTHTRN, disputes to DISPTRN) with IMAGES(*BOTH) on CCJRN, an account's balance is fully reconstructable from the posted ledger for reconciliation and recovery. The CCGL non-watermark and CCDELQ non-increment behaviours (F.5) are the two items an operator must manage by discipline rather than rely on the code to guard.

F. Developer Reference ↑ top

The complete program surface, from src/sources.mjs. All objects are in library CARDCOR; source is held as JS string constants and loaded into the library's source physical files (QDDSSRC / QRPGLESRC / QCBLLESRC / QCLSRC / QSQLSRC) by src/seed.mjs.

F.1 Programs

CCISSUE (IN PACCTNO, PCARDNO, PEXPDT) — RPG
Issues a card onto an open account. Guards the account exists and is open (ASTAT='O'); idempotent (CHAIN(E) CARDR first, skip if the card already exists); writes a CARDMST row with CSTAT='A' for the caller-supplied card number.
CCAUTH (IN PACCTNO, PCARDNO, PAUTHDT, PAUTHAMT, PMCC; OUT POUTRSP) — RPG
Open-to-buy authorization. WOTB = CRLIMIT − ACCBAL − HELDAUTH (packed). Non-open account or amount > OTB → 'D'; else 'A' with HELDAUTH += AUTHAMT. Always writes an AUTHTRN row (A or D). Next AUTHID via committed MAX.
CCPOST — RPG
Pass over AUTHTRN; for each AUTHRSP='A': write POSTTRN 'P', ACCBAL += AUTHAMT, HELDAUTH −= AUTHAMT (floored 0), flip 'A'→'S'. DSPLYs SETTLED/SKIP. Seeds POSTID once from committed MAX.
CCPAY (IN PACCTNO, PPAYAMT) — RPG
Reduces ACCBAL by the payment, floored at 0 (an overpayment zeroes the balance; the excess is not carried as a credit — documented simplification), writes a POSTTRN 'Y' row with a negative TRNAMT. Guards the account exists.
CCFIN — RPG
Pass over ACCTMST; for open, non-zero accounts: WADB=(LASTBAL+ACCBAL)/2, WRATE=APR/1200, WFC=%DECH(WADB×WRATE); if >0, write POSTTRN 'I' and add to balance. Nested if/else only.
CCDELQ — RPG
Pass over DELQLF: ACCBAL>CRLIMIT → over-limit fee (OVL); else DELQBKT≥1 → late fee (LTE). Writes POSTTRN 'F', adds to balance, freezes (ASTAT='F') if DELQBKT≥3. Note: never increments DELQBKT (F.5).
CCSTMT (IN WCYCLE) — RPG
Cuts statements for open non-zero accounts: min pay = greater of floor (MIN FEEAMT) or pct (MIN FEEFCTR), capped at the balance; writes STMTHDR, sets MINPAY and LASTBAL=NEWBAL, prints STMTP spool.
CCGL — RPG (embedded SQL, commitment control)
Pass over POSTTRN; maps each row to a balanced DR/CR pair by TRNTYPE (P/Y/F/I), inserts two GLENTRY rows using %ABS(TRNAMT), ends with EXEC SQL COMMIT. No period watermark (F.5).
CCDISP (IN PACCTNO, PPOSTID, PDISPAMT, PREASON; OUT POUTRSP) — RPG
Opens a dispute against a posted purchase: validates the account and that POSTID exists and is 'P'; writes DISPTRN 'O', a POSTTRN 'C' provisional credit (negative amount), and reduces ACCBAL by the dispute amount (Reg-Z provisional credit). PPOSTID is packed to match the packed caller. POSTTRN opened UF A (both CHAIN and WRITE).
CCCHGBK (IN PDISPID, POUTCOME; OUT POUTRSP) — RPG
Resolves an open dispute: 'W' won → DISPTRN 'O'→'W', balance unchanged; 'L' lost → write POSTTRN 'B' rebill (+amount), restore ACCBAL, DISPTRN 'O'→'L'. Guards the dispute exists and is still open. Nested if/else only.
CCACCMNT (IN PACTION + fields; OUT POUTBAL, POUTSTAT, POUTRSP) — RPG
Account-master maintenance: 'A' add (guarding CHAIN, reject if exists), 'C' change (limit/APR/status), 'I' inquire (balance/status out), 'X' close (ASTAT='C'). Every UPDATE re-CHAINs first.
CCACCINQ / CCTRNINQ / CCMENUP — RPG (5250)
Account inquiry, transaction-subfile inquiry, and the routing menu (B.2).
CCACCRPT — COBOL (ILE)
COMP-3 FD over ACCTMST byte-for-byte; totals ACCBAL for 'O' accounts; EXEC SQL SUM over GLENTRY receivable DR rows; “IN BALANCE” cross-check. Zoned nS0 map to PIC S9(n) DISPLAY, packed nP2 to COMP-3.
CCSETUP / CCNIGHT — CL
CCSETUP: single build entry point — DLTF/CRTPF/CRTLF/CRTDSPF/CRTPRTF/RUNSQLSTM, CRTBNDRPG/CRTBNDCBL/CRTCLPGM for every program, then CRTJRNRCV/CRTJRN/STRJRNPF (AUTHTRN, POSTTRN, DISPTRN to CCJRN). CCNIGHT: creates CCNITEQ and SBMJOBs the five night steps.

F.2 The next-id idiom & embedded SQL

Every arrival-sequence id (AUTHID, POSTID, DISPID) is generated with embedded SQL, not a native reposition:

-- once at entry for a multi-write batch, then bump a local counter per WRITE
exec sql select coalesce(max(postid),0) into :wnewid from cardcor.posttrn;
wnewid += 1;

This is a documented design-around (PLAN.md §5): a SETGT *HIVAL / READP next-id generator over an output-opened file does not see rows written earlier in the same job, so it returned 1 on every call after the first and the resulting duplicate-key WRITEs were silently dropped. Embedded-SQL MAX always sees committed rows. Multi-write batch programs (CCPOST/CCFIN/CCDELQ) query MAX once then bump locally within the run. Other embedded-SQL idioms: CCGL's two INSERTs + COMMIT, and CCACCRPT's SELECT COUNT/SUM ... INTO.

Related quirk (PLAN.md): a packed(8:0) CALL argument passed into a zoned 8S0 callee parameter is corrupted (read as the wrong representation). Because CCDISP/CCCHGBK use the id as a CHAIN key, those id parameters are declared packed (PPOSTID, PDISPID) to match the natural packed caller — a host-language-type-match discipline, not just a width match.

F.3 Journaling & commitment control

F.4 TRNTYPE / status codes

CodeFieldMeaning
PPOSTTRN.TRNTYPEPurchase settlement (CCPOST).
YPOSTTRN.TRNTYPEPayment received (CCPAY; negative amount).
FPOSTTRN.TRNTYPEFee — late or over-limit (CCDELQ).
IPOSTTRN.TRNTYPEInterest / finance charge (CCFIN).
CPOSTTRN.TRNTYPEDispute provisional credit (CCDISP; negative amount).
BPOSTTRN.TRNTYPEDispute rebill (CCCHGBK lost; +amount).
A / D / SAUTHTRN.AUTHRSPApproved / declined / settled (A flipped to S by CCPOST).
O / W / LDISPTRN.DSTATDispute open / won / lost.
O / F / CACCTMST.ASTATAccount open / frozen / closed.
A / BCARDMST.CSTATCard active / blocked.
A / R / C / I / XCCACCMNT POUTRSPAdd / reject / change / inquire / close response.

F.5 Known simplifications & gaps (honest)

Grounded in PLAN.md §6 and the source; all confirmed correct-as-designed or documented, none a platform bug hidden from the operator:

Platform quirks the app is coded around (not fixed in the engine): nested if/else only (free-form elseif mis-scopes its END-IF); free-form CHAIN can't reference a fixed-form KLIST; output-opened-file read invisibility (hence embedded-SQL MAX); a native WRITE against an input-only (IF) F-spec is silently lost (hence UF A opens); PRTF field-position digits end at DDS col 44; packed/zoned parameter-type match for CHAIN keys.

G. Glossary ↑ top

ADB — Average Daily Balance
The balance a finance charge is computed on. CARDCOR/i uses a two-point proxy (LASTBAL+ACCBAL)/2 (not true per-day tracking), × the monthly periodic rate.
APR — Annual Percentage Rate
The annual interest rate, stored as a percent (18.00 = 18%). The monthly periodic rate is APR/1200.
Authorization / open-to-buy
A real-time approve/decline of a purchase against open-to-buy = CRLIMIT − ACCBAL − HELDAUTH. An approval places a HELDAUTH hold until settlement releases it.
Chargeback
Resolution of a dispute in the cardholder's favour ('W' won) — the provisional credit becomes permanent. The opposite ('L' lost) rebills the amount.
Commitment control
Transactional grouping of SQL changes under a commitment definition, finalised by COMMIT (or undone by ROLLBACK). CARDCOR/i uses it on the GL path (CCGL), where the emulator honors it.
Delinquency bucket (DELQBKT)
How many cycles past due an account is; drives late-fee assessment and (at ≥3) the freeze path. Note: CCDELQ does not itself advance this value (F.5).
Dispute / provisional credit
A cardholder challenge to a posted purchase. CCDISP opens it (DISPTRN 'O'), posts a provisional credit (POSTTRN 'C') and drops the balance while it is open (Reg-Z).
Finance charge
Interest assessed on the ADB (CCFIN), written as a POSTTRN 'I' row and added to the balance.
HELDAUTH (held authorization)
The sum of open authorization holds on an account; it reduces open-to-buy until each auth settles (CCPOST releases the matching amount).
Idempotent
Safe to run again with the same result. CCPOST (auth marker 'A'→'S') and CCISSUE (dup CHAIN) are idempotent; CCGL/CCFIN/CCDELQ are not (run once per cycle).
Journaling (CCJRN)
Recording before/after images of row changes to a journal. The three ledgers (AUTHTRN, POSTTRN, DISPTRN) are journaled with IMAGES(*BOTH).
MCC — Merchant Category Code
A 4-character code classifying the merchant, stored on each authorization.
Minimum payment
The least the cardholder must pay this cycle: the greater of a floor (25.00) or a percentage (2%) of the new balance, capped at the balance (CCSTMT).
Open-to-buy (OTB)
CRLIMIT − ACCBAL − HELDAUTH — the amount still available to authorize.
Posting / settlement
Turning an approved authorization into a real ledger entry: CCPOST writes POSTTRN 'P', raises ACCBAL and releases the hold.
PRTF / spool
A printer file (STMTP) and the spooled output it produces — here the printed statement cut by CCSTMT.
SBMJOB / *JOBQ
Submit Job / job queue — the IBM i mechanism to queue a program as a batch job. CCNIGHT submits the night chain onto CCNITEQ.
Subfile (SFL/SFLCTL)
A 5250 display construct listing many rows on one screen. CCTRNINQ loads a subfile of an account's posted transactions from POSTLF.
TRNTYPE
The one-character posted-transaction type on POSTTRN: P/Y/F/I/C/B (F.4).