BILLIB/i — Customer Billing & GL Posting (ILE COBOL)

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

BILLIB/i is an ILE COBOL customer-billing / general-ledger reference application: a batch billing cycle that discounts each open invoice, updates the customer's balance and posts balanced GL entries; a CL-driven online customer-maintenance program reached by parameter; and an embedded-SQL aging report. It is deliberately a language-reference estate — each program exists to exercise one facet of ILE COBOL on IBM i (LINKAGE / PROCEDURE DIVISION USING, COBOL→*PGM CALL, ASSIGN TO DATABASE- with keyed and sequential EXTFH I/O, COPY DDS-ALL-FORMATS, embedded EXEC SQL, and the CRTCBLMOD+CRTPGM bind path) — while still reading as a real billing/GL app. This manual serves the operator who runs the cycle and the developer maintaining the programs. It is grounded entirely in the committed source (cobol-app/test/billing_cycle.mjs and cobol-app/test/edge_probes.mjs, which carry the COBOL/DDS/CL source as members plus the driver-computed oracles). Everything runs in library BILLIB.

Contents

A. Overview & Architecture ↑ top

A.1 What it does

BILLIB/i runs the everyday work of a small customer-billing / receivables shop:

A.2 A COBOL language-reference estate

Honest statement: BILLIB/i was built as a reference application that exercises the ILE COBOL layer of the SteelFrame X engine. It is a genuine, self-consistent billing/GL app — the balances, discounts and GL both-sides-net-to-zero are real and hand-checked — but its shape was chosen so each program demonstrates a distinct i-COBOL feature rather than to cover every edge of a production billing system. Two consequences an operator should know up front:

Section F maps each program to the exact ILE COBOL feature it demonstrates.

A.3 Component & flow

  ONLINE / CL-DRIVEN            BATCH (billing cycle)          REPORTING
  -----------------            ---------------------          ---------
  CUSTMCL (CLP)                BILLPOST (CBLLE)               AGERPT (CBLLE + EXEC SQL)
    CALL PARM                    OPEN INPUT   INVHPF            cursor over INVHPF
      |                          OPEN I-O     CUSTMPF           GROUP BY / HAVING COUNT(*)>1
      v                          OPEN EXTEND  GLPF              DISPLAY per customer
  CUSTMNT (CBLLE)                for each OPEN invoice:
    LINKAGE / PROCEDURE           CALL "RATELOOK" -----> RATELOOK (CBLLE)
    DIVISION USING                  (C-CL IAMT WS-NET)     packed-decimal discount
    keyed READ/REWRITE/           ADD WS-NET TO C-BAL
    DELETE on CUSTMPF             REWRITE CUST-REC (keyed I-O)
                                  WRITE GL-REC x2 (AR debit / REV credit)
                                  WRITE report line (PRINTER-QSYSPRT)
                                        |
                                        v
                          CUSTMPF (balances)  GLPF (postings)  QSYSPRT (spool)
                             journaled           journaled
                          BILLJRN / BILLRCV  <-- STRJRNPF on GLPF + CUSTMPF

  GLTOTMOD --CRTCBLMOD--> *MODULE --CRTPGM--> GLTOTPGM   (sequential count of GLPF)

A single billing event flows: BILLPOST reads an OPEN INVHPF row → keyed-reads the matching CUSTMPF customer → CALLs RATELOOK for the net amount → ADDs net to the customer balance and REWRITEs the record → WRITEs two GL rows (net debit to AR1000, net credit to RV2000) → the writes to GLPF and CUSTMPF land in the BILLJRN journal, and a report line is spooled to QSYSPRT.

A.4 Object inventory

ObjectTypeRole
CUSTMPFPF (DDS)Customer master, UNIQUE keyed on CUSTID.
INVHPFPF (DDS)Invoice header, arrival sequence.
RATEPFPF (DDS)Discount-rate table (see D — rate logic lives in RATELOOK).
GLPFPF (DDS)GL postings, arrival sequence, signed amounts.
RATELOOKCBLLE *PGMPacked-decimal discount routine; called by BILLPOST.
BILLPOSTCBLLE *PGMBatch billing cycle (the heart of the app).
CUSTMNTCBLLE *PGMParameter-driven customer maintenance (I/U/D).
CUSTMCLCLP *PGMCL wrapper calling CUSTMNT with CALL PARM.
AGERPTCBLLE *PGMEmbedded-SQL aging/aggregation report.
GLTOTMOD*MODULECRTCBLMOD module (GL row count).
GLTOTPGM*PGMCRTPGM-bound program from GLTOTMOD.
BILLJRNJournalJournals GLPF + CUSTMPF writes.
BILLRCVJrn receiverReceiver for BILLJRN.
QCBLLESRCSource PFILE COBOL source members.
QDDSSRCSource PFDDS source members.
QCLSRCSource PFCL source members.

The estate is 4 DDS physical files + 6 COBOL/CL programs + 1 bound module/program pair, journaled, driven from a single job (BILLJOB, user QPGMR). Sections D and F expand each.

B. Online Transactions & Screens ↑ top

B.1 The command/entry line

BILLIB/i has no CICS transaction identifiers and no menu. On IBM i each program is reached by name — from a 5250 command-entry line, from a CL program, or from a JOBQ/scheduler for the batch job. The job's library list must include BILLIB; the tested job runs with LIBL = QSYS QGPL BILLIB QTEMP and CURLIB = BILLIB (job BILLJOB, user QPGMR).

To do thisType on the command line
Run the billing cycleCALL BILLIB/BILLPOST (or SBMJOB it)
Maintain a customer directly (I/U/D)CALL BILLIB/CUSTMNT PARM(…)
Maintain a customer via the CL wrapperCALL BILLIB/CUSTMCL
Run the aging reportCALL BILLIB/AGERPT
Reconcile the GL row countCALL BILLIB/GLTOTPGM
Look up the discount net directlyCALL BILLIB/RATELOOK PARM(…)

BILLPOST, AGERPT and GLTOTPGM take no parameters — a bare CALL (or SBMJOB) drives them. CUSTMNT and RATELOOK are parameter programs (see B.2 and F).

B.2 Online customer maintenance (CUSTMNT / CUSTMCL)

CUSTMNT is the closest thing BILLIB/i has to an “online transaction”: a parameter-driven maintenance program over the customer master. It declares a LINKAGE SECTION and a PROCEDURE DIVISION USING five parameters, opens CUSTMPF I-O with ACCESS MODE IS DYNAMIC and RECORD KEY IS C-ID, keyed-READs the requested customer, and dispatches on the action code:

ParameterPICDirMeaning
LK-ACTIONX(1)inU update credit limit, D delete, other = inquire.
LK-CUSTIDX(6)inCustomer id to act on (moved to key C-ID).
LK-NEWLIMS9(7)V99 COMP-3inNew credit limit (used on action U).
LK-OUTSTATX(2)outFILE STATUS of the keyed operation (00 ok, 23 not found).
LK-OUTBALS9(7)V99 COMP-3outCurrent customer balance, echoed by reference.

CUSTMCL is the CL front door: it DCLs the five variables, issues CALL PGM(BILLIB/CUSTMNT) PARM(&ACTION &CUSTID &NEWLIM &OUTSTAT &OUTBAL), then SNDPGMMSGs CUSTMCL STATUS=[nn] BAL=[amt] — demonstrating the CL→COBOL CALL PARM path with a numeric *DEC(9 2) and character parameters passed by reference and read back after the call.

The by-reference contract is the point of the demonstration: CUSTMNT writes its results into the caller's LK-OUTSTAT/LK-OUTBAL storage, and the CL wrapper (or a direct caller) reads the updated values after the CALL returns. The packed COMP-3 balance survives the CL *DEC↔COBOL COMP-3 boundary.

B.3 Honest note: no 5250 display file

There is no DDS display file and no subfile in BILLIB/i. “Online” here means parameter-driven, not screen-driven: an operator or a driving program supplies the five parameters and reads the two output parameters back. There is likewise no maker–checker authorization — a CUSTMNT update or delete is applied immediately on the keyed file. The control model the app actually provides is journaling: CUSTMPF (and GLPF) are under BILLJRN, so every REWRITE/DELETE/WRITE is captured for after-the-fact audit and recovery. Section E covers the operational use of that trail.

C. Batch Jobs & the Billing Cycle ↑ top

The billing cycle is a single batch program, BILLPOST, plus two supporting programs run around it (AGERPT for reporting, GLTOTPGM for GL reconciliation). None take parameters; each is driven by a bare CALL or SBMJOB. The cycle reads its work from the data itself — every INVHPF row whose ISTAT = 'O' (open) — so there is no control row to advance; you seed/refresh invoices, then run.

-- submit the (parameterless) billing cycle as a batch job
SBMJOB CMD(CALL PGM(BILLIB/BILLPOST)) JOB(BILLPOST)

-- then the aging report and GL reconciliation
CALL BILLIB/AGERPT
CALL BILLIB/GLTOTPGM

C.1 Full batch program set

ProgramPurposeFiles / callsInputsOutputs
BILLPOST Discount & post every OPEN invoice; update balances; write GL. INPUT INVHPF (seq); I-O CUSTMPF (keyed); EXTEND GLPF (seq); PRINTER-QSYSPRT; CALL RATELOOK. OPEN invoices in INVHPF; customer classes in CUSTMPF. Updated CBAL (REWRITE); 2 GL rows per invoice; report to QSYSPRT; DISPLAY "BILLPOST N=nnn BAD=nnn".
AGERPT Open-amount aggregation per customer (>1 open invoice). Embedded SQL cursor over INVHPF (GROUP BY/HAVING/ORDER BY); COMMIT. OPEN invoices in INVHPF. DISPLAY "AGE cust TOT=amt CNT=n" per qualifying customer, then AGERPT DONE.
GLTOTPGM Count GL postings (reconciliation). INPUT GLPF (seq). Built CRTCBLMOD→CRTPGM. GLPF rows. DISPLAY "GLROWS=n" (expected 8 after a 4-invoice run).

C.2 The billing cycle in detail (BILLPOST)

BILLPOST opens four files with three different I/O disciplines — OPEN INPUT INVF (sequential), OPEN I-O CUSTF (keyed dynamic), OPEN EXTEND GLF (append), OPEN OUTPUT RPTF (printer) — then loops the invoice file. For each ISTAT = 'O' invoice it:

  1. MOVE ICUSTID TO C-ID and keyed-READ CUSTF. If the customer is missing (WS-CUSTFS ≠ '00') it bumps WS-BADCUST and skips.
  2. CALL "RATELOOK" USING C-CL IAMT WS-NET — the discounted net comes back by reference.
  3. ADD WS-NET TO C-BAL then REWRITE CUST-REC — the balance update via keyed I-O.
  4. WRITE GL-REC twice: AR1000 for +WS-NET (debit) and RV2000 for 0 - WS-NET (credit) — so every invoice contributes a balanced pair and the GL nets to zero.
  5. STRING a report line (INVNO ICUSTID NET=edited) and WRITE it to the printer file.

At end it prints a POSTED= BADCUST= summary line and DISPLAYs BILLPOST N=nnn BAD=nnn.

Expected (4 OPEN invoices, all customers present):
  BILLPOST N=004 BAD=000
  C00001 bal = 900.00 + 225.00 = 1125.00   (I00001 + I00003, class A 10%)
  C00002 bal = 475.00                       (I00002, class B 5%)
  C00003 bal = 720.00                       (I00004, class A 10%)
  GLPF   = 8 rows, SUM(GLAMT) = 0.00        (4 debits + 4 credits)
Idempotency: BILLPOST does not flip invoices out of OPEN status — INVHPF is read-only in this pass by design. Re-running it therefore re-bills the same invoices (balances grow again, GL doubles). Treat a billing run as a one-shot per invoice set; to re-run cleanly, reset the seed data first. This is called out in the source comments as a deliberate reference-estate simplification.

C.3 Edge-probe programs

edge_probes.mjs ships a second library (EDGELIB, over an ORDPF orders file) whose programs are not part of the billing cycle but exercise engine corners in COBOL: ORDRD (a sequential reader ridden by OPNQRYF/QRYSLT then CLOF), DYNCLR/DYNTGT (dynamic CALL by identifier variable), CANCTEST/STATEFUL (COBOL CANCEL re-init semantics), and NEGDISP (negative COMP-3 through a numeric-edited trailing-sign PICTURE). They are documented for developers in F.5.

D. Data Files (data dictionary) ↑ top

All files are DDS physical files in library BILLIB, created with CRTPF from members in QDDSSRC, and consumed by the COBOL programs via ASSIGN TO DATABASE- and (for INVHPF) COPY DDS-ALL-FORMATS OF INVHPF. Money fields are packed 9P 2 (COMP-3 in COBOL); ids are fixed Alpha.

CUSTMPF — Customer master (UNIQUE key CUSTID)

FieldDDS typeCOBOL PICMeaning
CUSTID6AX(6)Customer id (unique key), e.g. C00001.
CUSTNM20AX(20)Customer name.
CUSTCL1AX(1)Customer class — drives the discount (A=10%, B=5%, else 0%).
CRLIM9P 2S9(7)V99 COMP-3Credit limit (maintained by CUSTMNT).
CBAL9P 2S9(7)V99 COMP-3Customer balance (billed up by BILLPOST).

Seed: C00001 ACME CORP / A / 5000.00, C00002 BETA LLC / B / 2000.00, C00003 GAMMA INC / A / 1000.00 — all opening balance 0.00.

INVHPF — Invoice header (arrival sequence)

FieldDDS typeCOBOL PICMeaning
INVNO6AX(6)Invoice number, e.g. I00001.
ICUSTID6AX(6)Owning customer id (matched to CUSTMPF.CUSTID).
IAMT9P 2S9(7)V99 COMP-3Gross (pre-discount) invoice amount.
ISTAT1AX(1)Status — O (open) is what BILLPOST/AGERPT select on.

Seed: I00001 C00001 1000.00 O, I00002 C00002 500.00 O, I00003 C00001 250.00 O, I00004 C00003 800.00 O. Read via ORGANIZATION IS SEQUENTIAL with COPY DDS-ALL-FORMATS.

GLPF — General-ledger postings (arrival sequence)

FieldDDS typeCOBOL PICMeaning
GLACCT6AX(6)Account — AR1000 (receivable, debit) or RV2000 (revenue, credit).
GLINVNO6AX(6)Source invoice number.
GLAMT9P 2S9(7)V99 COMP-3Signed amount: net debit (+) to AR, net credit (−) to REV.

Written OPEN EXTEND (append). Two rows per posted invoice; across a run the signed GLAMT sums to 0.00 — the double-entry invariant BILLPOST maintains.

RATEPF — Discount-rate table (UNIQUE key on class)

The application's file list names a RATEPF discount-rate table keyed on customer class. In the committed reference build the discount logic itself lives in RATELOOK (class A→10%, B→5%, else 0%, as literals), so the running billing cycle derives its rate from the customer class through that program rather than by reading a rate row. RATEPF is the documented place a data-driven rate table would live; the reference estate keeps the rate in code to demonstrate COBOL-to-COBOL packed-decimal math.

Journaling — BILLJRN / BILLRCV

GLPF and CUSTMPF are placed under journal BILLJRN (receiver BILLRCV) via STRJRNPF before the cycle runs. A billing run therefore lands 8 PT (put/write) entries on GLPF and update entries on CUSTMPF — the audit/recovery trail the tests assert against.

Relationships

E. Operations Runbook ↑ top

E.1 Run the billing cycle

  1. Confirm the job's library list includes BILLIB and the seed/updated data is in place (customers in CUSTMPF, OPEN invoices in INVHPF).
  2. Confirm journaling is active: STRJRNPF on GLPF and CUSTMPF under BILLJRN (done once at setup).
  3. Submit the cycle: SBMJOB CMD(CALL PGM(BILLIB/BILLPOST)) (or a bare CALL).
  4. Check the DISPLAY / spooled report (post-checks below).
  5. Run CALL BILLIB/AGERPT for the per-customer open-amount report.
  6. Run CALL BILLIB/GLTOTPGM to reconcile the GL row count.
  7. Handle customer maintenance as needed: CALL BILLIB/CUSTMCL (CL wrapper) or a direct CALL BILLIB/CUSTMNT PARM(…).

Post-checks after BILLPOST:

E.2 Verified figures (20/20 checks)

Every figure below is a driver-computed oracle asserted against the engine's actual output. The two drivers report 14/14 (billing cycle) and 6/6 (edge probes) — 20/20 total.

#CheckExpected
1RATELOOK direct, class A on 1000.00net 900.00 (packed COMP-3)
2BILLPOST ranBILLPOST N=004 BAD=000
3Customer balances (keyed REWRITE)1125.00 / 475.00 / 720.00
4GL postings8 rows, sum 0.00
5GL writes journaled8 PT entries on GLPF
6Customer REWRITEs journaled≥3 update entries on CUSTMPF
7Printer report spooledheader + 4 lines + POSTED=004 BADCUST=000
8CUSTMNT direct update (by-ref)status 00, balance 475.00 echoed
9CUSTMNT REWRITE landedC00002 limit 2500.00, balance 475.00 untouched
10CUSTMCL (CL CALL PARM) deletestatus 00 echoed to CL var
11DELETE removed the rowC00003 gone; 2 customers remain
12Re-DELETE of missing keyFILE STATUS 23 (not silent success)
13AGERPT embedded-SQL aggregationAGE C00001 TOT=1250.00 CNT=2, then AGERPT DONE
14CRTCBLMOD+CRTPGM bind (GLTOTPGM)GLROWS=8
15Probe1a unfiltered ORDRD4 rows, total 1370.00
16Probe1b OPNQRYF QRYSLT (NE only)2 rows, total 1200.00
17Probe1c after CLOFreverts to 4 rows, 1370.00
18Probe2 dynamic CALL by identifierDYNCLR SAW [DYNAMICV]
19Probe3 COBOL CANCEL re-initCOUNT 1, 2, then reset to 1 (not silent-wrong 3)
20Probe4 negative COMP-3 edited PIC1234.56- (trailing minus)
Check 13 also proves the deliberate no-cascade design: after C00003's master row is deleted (checks 10–11) its invoice I00004 still exists in INVHPF, but C00003 has only one open invoice so it does not qualify for the HAVING COUNT(*) > 1 report — only C00001 (I00001 + I00003 = 1250.00 gross) appears.

E.3 Failure & re-run rules

SituationBehaviourAction
Missing customer for an invoiceBILLPOST bumps BAD and skips (no post, no GL).Add the customer to CUSTMPF, re-seed and re-run; the BAD= count flags it.
Re-running BILLPOSTNot idempotent — INVHPF stays OPEN, so balances re-grow and GL doubles.Reset seed data (drop/reload CUSTMPF balances + GLPF) before a re-run.
CUSTMNT on a missing keyREAD fails; status 23 returned, balance 0.Non-00 LK-OUTSTAT surfaces to the caller; verify the id and retry.
Re-DELETE an already-deleted keyReturns status 23, no error.Treat 23 as “already gone”; safe.
AGERPT SQL errorWHENEVER SQLERROR GO TO SQL-ERR DISPLAYs SQLERR nnn and GOBACKs.Read the SQLCODE; fix data/schema and re-run (AGERPT is read-only + COMMIT, safe to repeat).
GL row count offGLTOTPGM DISPLAYs GLROWS=n≠expected.Compare to 2×(posted invoices); investigate a partial BILLPOST run via the journal.
Because GLPF and CUSTMPF are journaled to BILLJRN, a billing run's writes and balance updates are fully reconstructable after the fact for reconciliation and recovery, even though the app has no maker–checker gate.

F. Developer Reference ↑ top

Each program and the exact ILE-COBOL-on-i feature it demonstrates, grounded in the source members in cobol-app/test/billing_cycle.mjs and edge_probes.mjs. All objects are in BILLIB (edge probes in EDGELIB). Compiled with CRTBNDCBL (COBOL), CRTCLPGM (CL), and the module path CRTCBLMOD+CRTPGM.

F.1 RATELOOK — COBOL→COBOL CALL, packed-decimal math

PROGRAM-ID RATELOOK — PROCEDURE DIVISION USING LK-CLASS LK-AMT LK-NET
A pure LINKAGE/no-file subroutine. Chooses a discount from LK-CLASS (A→10.00, B→5.00, else 0.00 into WS-DISC PIC S9(3)V99 COMP-3), then COMPUTE LK-NET = LK-AMT - (LK-AMT * WS-DISC / 100) — all packed COMP-3. Ends with EXIT PROGRAM (a called *PGM). Demonstrates: LINKAGE SECTION + PROCEDURE DIVISION USING, COBOL-to-COBOL CALL by reference (BILLPOST calls it), and packed-decimal arithmetic. Verified: class A on 1000.00 → 900.00.

F.2 BILLPOST — multi-file EXTFH I/O + printer + CALL

PROGRAM-ID BILLPOST — the batch cycle
Four SELECT … ASSIGN TO clauses: DATABASE-INVHPF (SEQUENTIAL), DATABASE-CUSTMPF (INDEXED, DYNAMIC, RECORD KEY C-ID), DATABASE-GLPF (SEQUENTIAL, opened EXTEND), and PRINTER-QSYSPRT. The invoice FD uses COPY DDS-ALL-FORMATS OF INVHPF; the others are hand-coded records. The loop keyed-reads the customer, CALLs RATELOOK, REWRITEs the balance, WRITEs two GL rows, and WRITEs report lines with STRING + numeric-edited WS-ED PIC Z(6)9.99. Demonstrates: keyed and sequential EXTFH I/O in one program, OPEN EXTEND, printer file I/O, COPY DDS-ALL-FORMATS, FILE STATUS checking, and calling another *PGM. Every write is journaled to BILLJRN.

F.3 CUSTMNT & CUSTMCL — LINKAGE + CL CALL PARM

PROGRAM-ID CUSTMNT — PROCEDURE DIVISION USING (5 parms)
Keyed I-O maintenance over CUSTMPF (DYNAMIC, RECORD KEY C-ID). Reads the requested customer and dispatches on LK-ACTION: UREWRITE new limit; DDELETE CUSTF RECORD; else inquire. Returns LK-OUTSTAT (FILE STATUS) and LK-OUTBAL by reference. Demonstrates: LINKAGE SECTION + PROCEDURE DIVISION USING with mixed char/packed parms passed by reference, and keyed READ/REWRITE/DELETE. FILE STATUS 23 is returned for a not-found key.
PROGRAM-ID CUSTMCL — CLP wrapper
DCLs the five parms (*CHAR and *DEC(9 2)), CALL PGM(BILLIB/CUSTMNT) PARM(…), then SNDPGMMSGs the echoed STATUS=[nn] BAL=[amt]. Demonstrates: the CL→COBOL CALL PARM boundary and reading COBOL OUT parms back into CL variables (incl. *DECCOMP-3).

F.4 AGERPT — embedded EXEC SQL

PROGRAM-ID AGERPT — SQL cursor aggregation
EXEC SQL INCLUDE SQLCA; WHENEVER SQLERROR GO TO SQL-ERR; a DECLARE C2 CURSOR FOR SELECT ICUSTID, SUM(IAMT), COUNT(*) … WHERE ISTAT='O' GROUP BY ICUSTID HAVING COUNT(*) > 1 ORDER BY ICUSTID; then OPEN / FETCH-loop (FETCH C2 INTO :WS-CUST, :WS-TOT:WS-IND, :WS-CNT, with a null indicator on the sum) / CLOSE / COMMIT. Demonstrates: embedded SQL in ILE COBOL — cursor, host variables, a null indicator variable, WHENEVER error branching, and COMMIT. Verified: only C00001 qualifies (TOT=1250.00, CNT=2).

F.5 GLTOTMOD/GLTOTPGM & the edge probes

GLTOTMOD → GLTOTPGM — CRTCBLMOD + CRTPGM
A sequential reader over GLPF counting rows into WS-N, built as a *MODULE (CRTCBLMOD) then bound to a *PGM (CRTPGM PGM(GLTOTPGM) MODULE(GLTOTMOD)). Demonstrates: the separate compile-then-bind ILE path (vs. the one-step CRTBNDCBL the other programs use). Sets RETURN-CODE. Verified: GLROWS=8.
ORDRD (+ OPNQRYF/CLOF) — probe 1
A COBOL ORGANIZATION SEQUENTIAL reader over ORDPF. An OPNQRYF … QRYSLT('OREG *EQ "NE"') rides the open, restricting the COBOL read to 2 rows (1200.00); CLOF then reverts it to the full 4 rows (1370.00). Confirms OPNQRYF is snapshot-scoped, not a stuck or silently-ignored filter.
DYNCLR/DYNTGT — probe 2
CALL WS-PGM where WS-PGM is an identifier (not a literal) resolves the target *PGM at run time and the by-ref update is visible: DYNCLR SAW [DYNAMICV].
CANCTEST/STATEFUL — probe 3
COBOL CANCEL "STATEFUL" between calls: the correct ILE semantics re-initialize the callee's WORKING-STORAGE, so the counter reads 1, 2, then 1 again after CANCEL (a silent no-op that left it at 3 would be flagged as wrong). Verified reset.
NEGDISP — probe 4
A negative COMP-3 (-1234.56) moved through a numeric-edited PIC Z(6)9.99- shows a trailing minus (1234.56-), not a dropped or garbled sign.

F.6 i-COBOL feature matrix

FeatureProgram(s)
LINKAGE SECTION + PROCEDURE DIVISION USINGRATELOOK, CUSTMNT, DYNTGT
COBOL→*PGM CALL (literal & by identifier)BILLPOST→RATELOOK; DYNCLR→DYNTGT
CL CALL PARM into COBOLCUSTMCL→CUSTMNT
ASSIGN TO DATABASE- keyed (INDEXED/DYNAMIC) I/OBILLPOST, CUSTMNT
ASSIGN TO DATABASE- sequential I/O + OPEN EXTENDBILLPOST (GLF), GLTOTMOD, ORDRD
PRINTER-QSYSPRT report I/OBILLPOST
COPY DDS-ALL-FORMATSBILLPOST (INVHPF)
Packed-decimal (COMP-3) math & edited PICTUREsRATELOOK, BILLPOST, NEGDISP
Embedded EXEC SQL (cursor, indicator, WHENEVER, COMMIT)AGERPT
CRTCBLMOD + CRTPGM bind pathGLTOTMOD/GLTOTPGM
OPNQRYF/QRYSLT over a COBOL sequential read; COBOL CANCELORDRD; CANCTEST/STATEFUL

G. Glossary ↑ top

ASSIGN TO DATABASE-
The COBOL SELECT clause tying a logical file to an IBM i physical/logical file by name (e.g. DATABASE-CUSTMPF), so COBOL EXTFH I/O reads/writes the DB file.
CBLLE / CRTBNDCBL
ILE COBOL source type / the one-step command that compiles and binds it to a *PGM.
COMP-3 (packed decimal)
A numeric storage format packing two digits per byte plus a sign nibble (DDS P). All money fields here are COMP-3; RATELOOK's discount math runs in it.
COPY DDS-ALL-FORMATS
A COBOL COPY that pulls the externally-described DDS record layout into an FD, so field names (e.g. INVNO, ISTAT) are usable without hand-coding the record.
CRTCBLMOD + CRTPGM
The two-step ILE build: compile source to a *MODULE, then bind one or more modules into a *PGM — contrasted with one-step CRTBNDCBL.
Double-entry / balanced GL
Each posted invoice writes a debit to AR1000 and an equal credit to RV2000, so the signed GL amounts sum to zero — the invariant BILLPOST maintains and GLTOTPGM helps reconcile.
EXTFH
The external file handler through which COBOL record I/O (READ/WRITE/ REWRITE/DELETE) reaches the IBM i database files.
FILE STATUS
The two-byte status COBOL sets after each I/O: 00 success, 23 record not found. CUSTMNT returns it to its caller as LK-OUTSTAT.
Indicator variable
An embedded-SQL companion host variable (:WS-TOT:WS-IND) signalling SQL NULL for the fetched column.
LINKAGE SECTION / PROCEDURE DIVISION USING
The COBOL mechanism for receiving parameters passed by reference from a caller (CL, another COBOL program, or a driver) — how RATELOOK and CUSTMNT take and return values.
OPEN EXTEND
Opening a sequential file to append records at the end — how BILLPOST adds GL postings.
OPNQRYF / QRYSLT / CLOF
Open Query File: a CL command that opens a filtered/derived view a COBOL program then reads; QRYSLT is its selection, CLOF closes it.
PRINTER-QSYSPRT
A COBOL printer file assigned to the system spool (QSYSPRT); BILLPOST writes its run report there.
SBMJOB
Submit Job — queues a program as a batch job, e.g. SBMJOB CMD(CALL PGM(BILLIB/BILLPOST)).
STRJRNPF / journal (BILLJRN)
Start Journaling a physical file / the journal object recording its changes (PT put entries, update entries) for audit and recovery.