AQUILA/i — Utilities Billing Core

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

AQUILA/i is a combined electric / water / gas utilities billing core: customer and service-point/meter masters, monthly meter reads, tiered/stepped-rate billing with a printed bill, cash receipts, delinquency & disconnect processing with a matching reconnect arc, late-fee and interest accrual, and a balanced GL posting feed. It is a native AS/400 estate — DDS physical and logical files, RPG (fixed-form and free-form) and ILE COBOL programs, one DB2 SQL table, CL for build and night batch, 5250 display files (including a subfile), a printer file, journaling and an object-authority restriction. This manual is the reference for the operator who runs the online screens and the night-batch chain, and for the developer maintaining the application. It is grounded entirely in the committed source (utilities-app/src/sources.mjs in library AQUILA, src/seed.mjs, and the test/aquila_*.mjs drivers).

Contents

A. Overview & Architecture ↑ top

A.1 What it does

AQUILA/i runs the monthly cycle of a combined-service (electric / water / gas) utility:

A.2 Native-AS/400 architecture

Unlike a two-layer SQL-PL application, AQUILA/i is a classic native IBM i estate: the business logic lives in the RPG and COBOL programs themselves, operating directly on DDS record-level files through keyed and arrival I/O (CHAIN, SETLL/READ, WRITE/UPDATE). Only two paths use embedded SQL — the GL posting program UTGLPST (DB2 INSERT/SUM under commitment control) and the COBOL report UTGLRPT (a cross-check SUM). Everything else is record-level RPG/COBOL.

Everything runs in library AQUILA (data root utilities-app/ibmidata-aquila*). The tested jobs run with LIBL = QSYS QGPL AQUILA QTEMP and CURLIB = AQUILA as QSECOFR.

A.3 Component & flow

  MAINTENANCE          ONLINE (5250)          BATCH (night chain, SBMJOB *JOBQ)
  -----------          -------------          --------------------------------
  UTMNT (CC/MC/CI/MI)  UTMENU                  UTBLGEN  --> UTBILL + UTBLIN + UTLEDG'B'
  UTRATLD (rate plan)    1 -> UTACCTIQ         UTCSHPST --> pay oldest bill, UTLEDG'P'
                         2 -> UTBILLIQ (SFL)   UTDELINQ --> flag D, disconnect, UTLEDG'A'
                                               UTFEECAL --> late fee+interest, UTLEDG'F'
                                               UTGLPST  --> balanced DR/CR into UTGLF (DB2)
                                               UTBILLPR --> bill spool (UTBILLP)
     reference: UTRATE (RATECD+TIERNO)         UTRECON  --> reconnect paid-up SP, UTLEDG'R'
            |                                  UTAGE    --> aged-AR buckets (report)
            v                                  UTGLRPT  --> COBOL cross-check (IN/OUT OF BALANCE)
  UTREAD (meter reads, journaled)  --UTBLGEN--> UTBILL --UTAGELF(by DUEDT)--> UTDELINQ/UTFEECAL/UTAGE
            |                                          \
            +--> every posting program APPENDS a keyed row to UTLEDG (journaled A/R audit trail)

A single billing event flows: meter reads land in UTREAD (journaled) → UTBLGEN walks each read against the UTRATE tiers, computes the whole bill in working storage, then writes UTBLIN detail lines, the UTBILL header, rolls up UTSVCPT.SPBAL, and appends a UTLEDG 'B' audit row. Downstream programs read bills through the aging logical file UTAGELF (keyed by DUEDT).

A.4 Object inventory

ObjectTypeRole
UTCUSTPFCustomer master.
UTSVCPTPFService-point master (customer + utility type + rate + meter + balance).
UTMETERPFPhysical meter master.
UTRATEPFTiered/stepped rate plan (keyed RATECD+TIERNO).
UTREADPF (journaled)Meter reads.
UTBILLPFBill header.
UTBLINPFBill tier-detail lines (keyed BILLNO+LINENO).
UTCASHPFCash receipts.
UTLEDGPF (journaled)A/R ledger / audit trail (keyed by LEDGSEQ).
UTBLINLLFUTBLIN keyed by BILLNO (subfile source).
UTAGELFLFUTBILL keyed by DUEDT (aging pass).
UTGLFDB2 tableGL posting feed (SQL DDL via RUNSQLSTM).
UTACCTD / UTBILLD / UTMENUDDSPFAccount inquiry / bill-inquiry subfile / operator menu.
UTBILLPPRTFThe printed bill.
UTRATLDRPGSeed the tiered rate plan.
UTMNTRPGCustomer/meter master maintenance (callable, dcl-pi).
UTBLGENRPGTiered billing engine.
UTCSHPSTRPGCash posting (partial-pay aware).
UTDELINQRPGDelinquency / disconnect.
UTFEECALRPGLate-fee + interest accrual.
UTRECONRPGReconnect a paid-up disconnected service point.
UTGLPSTSQLRPGLEGL posting under commitment control (**FREE + embedded SQL).
UTBILLPRRPGBill print (PRTF spool).
UTAGERPGAged-AR report.
UTACCTIQ / UTBILLIQ / UTMENURPG (WORKSTN)Account inquiry / bill-inquiry subfile / menu.
UTGLRPTILE COBOLGL cross-check report (COMP-3 FD + embedded SQL).
UTSETUP / UTNIGHTCLBuild/journal/authority/seed · night-batch chain.
UTJRN / UTRCV*JRN / *JRNRCVJournal + receiver over UTREAD and UTLEDG.

The catalogue is 8 PFs + 2 LFs + 1 DB2 table, 3 DSPFs + 1 PRTF, 13 RPG programs + 1 ILE COBOL + 2 CL, plus a journal/receiver pair. Sections D and F expand each.

B. Online Transactions & Screens ↑ top

B.1 The command/entry line

AQUILA/i has no CICS transaction identifiers and no menu-driven transid switch. On IBM i each program is reached by name from a 5250 command-entry line (or a JOBQ/scheduler for the batch jobs). 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 AQUILA — the tested jobs run with LIBL = QSYS QGPL AQUILA QTEMP and CURLIB = AQUILA.

To do thisType on the command line
Open the operator menu (routes to the two inquiries)CALL AQUILA/UTMENU
Account inquiry directlyCALL AQUILA/UTACCTIQ
Bill inquiry (subfile) directlyCALL AQUILA/UTBILLIQ
Maintain a customer or meterCALL AQUILA/UTMNT (with parameters — see below)
Build the whole app + seed the rate planCALL AQUILA/UTSETUP
Run one night-batch stepCALL AQUILA/UTBLGEN (or SBMJOB it)

UTMNT is the one online program that is parameter-driven rather than screen-driven: it is called with a dcl-pi entry list so a driver or the day-cycle can maintain masters without a green screen. Its first parameter pAction selects the operation:

pActionDoesKey parameters used
CCCreate/change a customer.pKey (first 6 = CUSTNO), pName, pCity, pState.
MCCreate/change a meter.pKey (METERNO), pMSpno, pMUtype, pMMult.
CIInquire a customer.pKey; returns pName/pCity/pState, pOutFound.
MIInquire a meter.pKey; returns pMSpno/pMUtype/pMMult, pOutFound.

UTMNT reports its result in pOutFound ('Y'/'N') and a 30-char pOutMsg (e.g. CUSTOMER CREATED, METER CHANGED, CUSTOMER NOT FOUND, BAD ACTION), and also DSPLYs one line so a batch driver can assert on it without a screen. For a change it re-CHAINs the full key immediately before the UPDATE so it never double-updates a stale read.

B.2 Menu & inquiry screens (UTMENU / UTACCTIQ / UTBILLIQ)

The interactive front end is three WORKSTN programs over three display files. UTMENU (format MENUFMT) offers two options and dynamically CALLs the matching inquiry program; an invalid option shows Invalid option and neither program is called; F3 ends the menu.

AQUILA/i Main Menu 1. Account Inquiry 2. Bill Inquiry Option . . . . : _ F3=Exit Enter=Select

Account inquiry — UTACCTIQ (format ACCTINQ)

A plain (non-subfile) screen. Key a service point in ISPNO, Enter: UTACCTIQ CHAINs UTSVCPT, then UTCUST for the name, and shows the customer, utility type, rate class, meter number, status and balance. A blank key prompts Enter a service point.; an unknown one shows Service point not found: <spno>.

Account Inquiry - AQUILA/i Service point: SP000101 Customer . . . : ACME MANUFACTURING Utility type . : E Rate class . . : ELE Meter number . : M0000101 Status . . . . : A Balance . . . : 84.50 Service point found. F3=Exit Enter=Inquire

Bill inquiry — UTBILLIQ (formats BSFL / BCTL)

The application's one subfile screen. Key a bill number in IBILLNO, Enter: UTBILLIQ CHAINs UTBILL for the header (service point, cycle, usage, status, total charge), then loads the bill's tier lines from UTBLINL (the LF over UTBLIN keyed by BILLNO) into the BSFL subfile via SETLL/READE, and displays them under control record BCTL (SFLPAG(10), SFLSIZ(20)). Indicators drive the subfile: 41=SFLDSP, 42=SFLDSPCTL, 43=SFLCLR, 44=SFLEND(*MORE). A blank key prompts Enter a bill number.; an unknown one shows Bill not found: <billno> and the subfile is cleared.

Bill Inquiry - AQUILA/i Bill number: B0001001 Svc point . . : SP000101 Cycle . . . . : 202607 Usage . . . . : 1200 Status . . . . : O Line Tier Usage Price Amount 1 1 000000500 0.1200 60.00 2 2 000000500 0.1500 75.00 3 3 000000200 0.2000 40.00 184.50 total charge Bill found. F3=Exit Enter=Inquire

Subfile columns (BSFL)

FieldType (DDS)Shows
SLINENO2Y 0 OBill line number.
STIER2Y 0 OTier number (LTIERNO).
SUSE9Y 0 OUsage billed in this tier (TIERUSE).
SPRC14A OPer-tier unit price (TIERPRC, rendered as char).
SAMT14A OTier line amount (LINEAMT, rendered as char).
The numeric subfile fields SLINENO/STIER/SUSE carry no EDTCDE/EDTWRD keyword, so 5250 renders them as raw zero-padded digits (e.g. usage 500 shows as 000000500, a zero-usage tier as 000000000) with no zero-suppression — the correct DDS rule for an unedited numeric output field. Price and amount are pre-formatted with %char() into character fields.

B.3 Controls & audit workflow (honest model)

Honest statement: AQUILA/i does not model a four-eyes maker–checker / separate-authorization workflow. There is no "one clerk posts, a second approves" step; a cash receipt is applied immediately by UTCSHPST, and delinquency/disconnect/reconnect happen automatically in the night batch. The control model the application does have is:

In sum, the control posture is immutable audit + journaling + object authority + state gating + a balanced control total, all in the data/logic layer, rather than a segregation-of-duties approval workflow.

C. Batch Jobs & the Periodic Cycle ↑ top

AQUILA/i's billing cycle runs as a night-batch chain: bill generation, cash posting, delinquency/disconnect, fee assessment, GL posting and bill print, with reconnect and the aged-AR report as adjuncts. None of the batch programs take CALL parameters — each opens the DDS files it needs, makes one pass, and closes them. Because rows written to an output-opened file may not be visible to a called subprogram until the writer closes it, the chain is not one CL calling several programs in-process; each step is a separate SBMJOB on a *JOBQ, so each program fully closes its output before the next opens it.

-- create the night-batch job queue, then submit the steps in order
CRTJOBQ JOBQ(AQUILA/NITEQ) TEXT('AQUILA night batch queue')
SBMJOB CMD(CALL AQUILA/UTBLGEN)  JOB(AQBLGEN) JOBQ(AQUILA/NITEQ) HOLD(*NO)
SBMJOB CMD(CALL AQUILA/UTCSHPST) JOB(AQCASH)  JOBQ(AQUILA/NITEQ) HOLD(*NO)
SBMJOB CMD(CALL AQUILA/UTDELINQ) JOB(AQDLQ)   JOBQ(AQUILA/NITEQ) HOLD(*NO)
SBMJOB CMD(CALL AQUILA/UTFEECAL) JOB(AQFEE)   JOBQ(AQUILA/NITEQ) HOLD(*NO)
SBMJOB CMD(CALL AQUILA/UTGLPST)  JOB(AQGLP)   JOBQ(AQUILA/NITEQ) HOLD(*NO)
SBMJOB CMD(CALL AQUILA/UTBILLPR) JOB(AQPRT)   JOBQ(AQUILA/NITEQ) HOLD(*NO)

The CL member UTNIGHT documents the same sequence as a single entry point (BILLGEN → CASH POST → DELINQUENCY → FEE → GL POST → bill print); the tested harness drives them as the separate SBMJOBs above.

C.1 Full batch program set

ProgramPurposeFiles read/writtenLedgerOutputs (DSPLY)
UTBLGEN Tiered billing engine — one pass over UTREAD, apply UTRATE tiers. Reads UTREAD, UTSVCPT, UTRATE; writes UTBILL, UTBLIN, updates UTSVCPT.SPBAL, appends UTLEDG. 'B' UTBLGEN GENERATED=n SKIPPED=n REJECTED=n
UTCSHPST Cash posting — one pass over UTCASH, apply each receipt to the oldest open bill. Reads UTCASH; forward-reads UTBILL; updates UTBILL + UTSVCPT; appends UTLEDG. 'P' UTCSHPST FULL=n PART=n NOBILL=n
UTDELINQ Delinquency / disconnect — flag overdue open bills, disconnect SP, flag customer. Reads UTAGELF; updates UTBILL, UTSVCPT, UTCUST; appends UTLEDG. 'A' UTDELINQ FLAGGED=n
UTFEECAL Late fee + interest on delinquent bills (packed-decimal calc). Reads UTAGELF; updates UTBILL + UTSVCPT balances; appends UTLEDG. 'F' UTFEECAL ASSESSED=n TOTFEE=n
UTGLPST GL posting under commitment control (embedded SQL); COMMIT only if balanced. SQL SUM over UTBILL/UTLEDG; INSERT into UTGLF (DB2). UTGLPST IN BALANCE DR=… CR=… COMMITTED (or OUT OF BALANCE … ROLLED BACK)
UTBILLPR Bill print — one PRTF page-set per bill (header, tier detail, total, due). Reads UTBILL + UTBLINL; writes UTBILLP spool. UTBILLPR PRINTED=n
UTRECON Reconnect — a paid-up disconnected SP is reconnected; customer restored if no other SP is still delinquent. Two passes over UTSVCPT; updates UTSVCPT, UTCUST; appends UTLEDG. 'R' UTRECON RECONNECTED=n
UTAGE Aged-AR report — bucket open vs delinquent balances. Reads UTAGELF only. UTAGE BILLS=n TOTBAL=n OPEN=n DLQ=n
UTGLRPT ILE COBOL cross-check — sum UTBILL charges (COMP-3 FD) + fees vs UTGLF A/R debit. Reads UTBILL; SQL SUM over UTLEDG + UTGLF. UTGLRPT … CROSS-CHECK: IN BALANCE / OUT OF BALANCE

C.2 The night-batch chain, step by step

1. Billing — UTBLGEN

One pass over UTREAD (arrival). For each read it derives a deterministic BILLNO = 'B' + last 7 digits of (READID + 10000000) (so READID 1001 → B0001001, READID 701 → B0000701); a CHAIN(EN) on the header skips an already-billed read (idempotent). It computes the whole bill in wide (11P2) working storage first — walking UTRATE tiers in TIERNO order, each tier billing MIN(remaining, band) × UNITPRC with the sentinel-topped last tier absorbing the rest, plus BASECHG on tier 1 — then, only if the computed charge fits the persistable 9P2 capacity, re-walks the tiers to write the UTBLIN lines, the UTBILL header (BSTAT='O', DUEDT = CYCLE×100 + 20), rolls up SPBAL, and appends the ledger 'B' row. An oversized read is a clean data-quality reject with nothing written (no torn write).

Tiered-billing worked example (from the day-cycle test):
  ELE 1200 kWh:  9.50 base + 500*.12 + 500*.15 + 200*.20 = 9.50 + 60 + 75 + 40 = 184.50
  WAT  400 CCF:  6.00 base + 300*.05 + 100*.08           = 6.00 + 15 + 8       =  29.00
  GAS  250 thm:  7.25 base + 200*.40 +  50*.55           = 7.25 + 80 + 27.50   = 114.75
  DSPLY: UTBLGEN GENERATED=3 SKIPPED=0 REJECTED=0

2. Cash posting — UTCSHPST

One pass over UTCASH (arrival). Each receipt is applied to the single oldest open bill for its service point — a forward READ over UTBILL selecting the first BSTAT IN ('O','D') row for that SPNO with a positive balance. A payment short of the balance is a partial (BSTAT='D', balance reduced); a full/over payment closes it (BSTAT='P', balance 0). SPBAL is reduced by the amount applied and a ledger 'P' row written. A receipt with no matching open bill increments NOBILL.

Example: ACME ELE 184.50, pay 100.00 (partial); RIVERSIDE GAS 114.75, pay 114.75 (full)
  ELE  -> BSTAT=D, BILLBAL=84.50, SPBAL 184.50 -> 84.50
  GAS  -> BSTAT=P, BILLBAL=0.00,  SPBAL 114.75 -> 0.00
  DSPLY: UTCSHPST FULL=1 PART=1 NOBILL=0

3. Delinquency / disconnect — UTDELINQ

One forward pass over the aging LF UTAGELF (keyed by DUEDT). Any bill BSTAT IN ('O','D') with a positive balance is delinquent: the bill is set 'D', its service point SPSTAT='D' (disconnected) and its customer CSTAT='D', and a ledger 'A' row is written.

Honest note on the cutoff. The source comment describes a "derived" due-date cutoff, but the implemented code applies no date/grace check at all — any open bill with BILLBAL > 0, even one cent, is flagged the first time UTDELINQ runs after billing, and it operates strictly per bill: a service point with even one unpaid weekly/monthly bill is disconnected and its customer flagged, regardless of other bills paid in full. This is a stale comment vs. a simpler/harsher implementation, not a functional defect; operators should pay a bill in full to keep a service point connected.

4. Late fee + interest — UTFEECAL

One pass over UTAGELF. For every BSTAT='D' bill still carrying a positive balance it assesses a flat $5.00 late fee plus monthly interest at 1.50% of the balance, computed in wide packed storage (15P4) then rounded half-up to cents. The fee is added to BILLBAL and SPBAL, and a ledger 'F' row is written. A per-bill idempotency guard scans UTLEDG for an existing 'F' row tagged with this bill's memo, so a bill is fee-assessed at most once per pass.

Example: bill 184.50 delinquent
  fee = 5.00 flat + round(184.50 * 0.0150 = 2.7675 -> 2.77) = 7.77
  BILLBAL 184.50 -> 192.27,  SPBAL grows by the same 7.77
  DSPLY: UTFEECAL ASSESSED=1 TOTFEE=7.77

5. GL posting — UTGLPST

Embedded SQL under commitment control (ctl-opt commit(*yes)). It sums total billed charges (SUM(CHARGE) over UTBILL), cash applied (ledger 'P') and fees (ledger 'F'), then inserts three balanced DR/CR pairs into UTGLF: billing DR A/R (120000) / CR Revenue (400000); cash DR Cash (100000) / CR A/R (120000); fee DR A/R (120000) / CR Fee Revenue (410000). It re-sums the batch just posted and COMMITs only if SUM(DR)=SUM(CR), else ROLLBACK.

Honest note on idempotency. UTGLPST posts the full cumulative all-time totals under a static BATCHID='GLB00001', with no delta/watermark tracking. Each individual run is internally balanced, but calling it more than once in a period re-posts the whole cumulative total again as new rows, inflating the running UTGLF A/R total. Run it exactly once per close. When it has been run more than once, UTGLRPT correctly reports OUT OF BALANCE — that is the cross-check working, not a defect in the report or the SQL engine.

6. Bill print — UTBILLPR

One pass over UTBILL; for each bill it writes the PRTF (UTBILLP) header, then the tier detail lines from UTBLINL (READE by BILLNO), the total charge and the due line, producing a UTBILLP spooled file. The spool shows the bill title, the total (e.g. 184.50) and per-tier prices (e.g. 0.1200).

Adjuncts — UTRECON, UTAGE, UTGLRPT

UTRECON closes the disconnect loop: pass 1 reconnects every disconnected service point whose balance is cleared (SPSTAT 'D' → 'A', ledger 'R'); pass 2 restores a customer to CSTAT='A' only if it has no other still-disconnected service point. It refuses to reconnect a service point that still owes. UTAGE buckets open vs delinquent balances for a report. UTGLRPT (ILE COBOL) totals UTBILL.CHARGE through a COMP-3 FD, adds ledger fees, and compares against the UTGLF A/R debit, printing IN BALANCE / OUT OF BALANCE.

C.3 Ordering, idempotency & dependencies

D. Data Files (data dictionary) ↑ top

All files are in library AQUILA, grounded in the DDS/SQL in src/sources.mjs. Type codes: nA = character, nS d = zoned decimal (n digits, d after point), nP d = packed decimal. Dates are stored as zoned integers in YYYYMMDD (or YYYYMM for a billing cycle). Unless noted, files are UNIQUE-keyed PFs.

UTCUST — Customer master (key CUSTNO)

FieldTypeMeaning
CUSTNO6ACustomer number (key).
CNAME25ACustomer name.
ADDR / CITY / ST / ZIP25A / 15A / 2A / 5AMailing address.
CSTAT1AStatus: A active, D delinquent.

UTSVCPT — Service-point master (key SPNO)

FieldTypeMeaning
SPNO8AService-point number (key), e.g. SP000101.
CUSTNO6AOwning customer.
UTYPE1AUtility type: E electric, W water, G gas.
RATECD3ARate class (ELE/WAT/GAS) → UTRATE.
METERNO8AMeter serving this point → UTMETER.
SPSTAT1AStatus: A active, D disconnected.
SPBAL9P 2Running service-point balance.

UTMETER — Physical meter master (key METERNO)

FieldTypeMeaning
METERNO8AMeter serial (key).
MSPNO8AService point the meter serves.
MUTYPE1AUtility type (E/W/G).
MINSTDT8S 0Install date (YYYYMMDD).
MMULT5P 2Dial multiplier (scaled-unit meters: consumption = raw-delta × multiplier).
MPRVRD9P 0Previous read value.
MSTAT1AStatus: A active, R removed.

UTRATE — Tiered rate plan (key RATECD + TIERNO)

FieldTypeMeaning
RATECD3ARate class (key part 1).
TIERNO2S 0Tier number within the class (key part 2).
TIERUB9P 0Tier upper bound (cumulative); the top tier carries a sentinel 999999999.
UNITPRC7P 4Per-unit price in this tier.
BASECHG7P 2Fixed monthly service charge (carried on tier 1 only).

Seeded plan (UTRATLD): ELE 1→ub500/0.1200/base9.50, 2→ub1000/0.1500, 3→sentinel/0.2000; WAT 1→ub300/0.0500/base6.00, 2→sentinel/0.0800; GAS 1→ub200/0.4000/base7.25, 2→sentinel/0.5500. (7 tiers: ELE=3, WAT=2, GAS=2.)

UTREAD — Meter reads (key READID; journaled)

FieldTypeMeaning
READID8S 0Read id (key); drives the derived BILLNO.
SPNO8AService point read.
CYCLE6S 0Billing cycle (YYYYMM).
PRIORRD / CURRRD9P 0Prior and current dial readings.
RUSAGE9P 0Consumption for the cycle (renamed from USAGE — see F.4).

UTBILL — Bill header (key BILLNO)

FieldTypeMeaning
BILLNO8ABill number (key), 'B'+7 digits, e.g. B0001001.
SPNO8AService point billed.
CYCLE6S 0Billing cycle (YYYYMM).
BUSAGE9P 0Usage billed (renamed from USAGE — see F.4).
CHARGE9P 2Total computed charge.
DUEDT8S 0Due date = CYCLE×100 + 20 (day 20 of the cycle month).
BSTAT1AO open, P paid, D delinquent.
BILLBAL9P 2Remaining bill balance.

UTBLIN — Bill tier lines (key BILLNO + LINENO)

FieldTypeMeaning
BILLNO / LINENO8A / 2S 0Bill + line number (key).
LTIERNO2S 0Rate tier this line represents.
TIERUSE9P 0Usage billed in this tier.
TIERPRC7P 4Unit price applied.
LINEAMT9P 2Line amount = TIERUSE × TIERPRC.

UTCASH — Cash receipts (key RCPTNO)

FieldTypeMeaning
RCPTNO8S 0Receipt number (key).
SPNO8AService point paid.
RCPTDT8S 0Receipt date (YYYYMMDD).
PAYAMT9P 2Payment amount.

UTLEDG — A/R ledger / audit trail (key LEDGSEQ; journaled)

FieldTypeMeaning
LEDGSEQ8S 0Ledger sequence (key); assigned from a per-program disjoint band (F.3).
SPNO8AService point.
LTYPE1AB bill, P payment, A adjustment (delinquency), F fee, R reconnect.
LDT8S 0Ledger date (YYYYMMDD).
LAMT9P 2Amount of the entry.
LMEMO20AFree-text memo (e.g. BILL B0001001, CASH RCPT, FEE …).

Authority: *PUBLIC is restricted to *CHANGE on UTLEDG (RVKOBJAUT *ALL then GRTOBJAUT *CHANGE) so the batch can add/update but not delete.

UTBLINL / UTAGELF — Logical files

UTBLINL is an LF over UTBLIN keyed by BILLNO — the subfile source for UTBILLIQ and the detail source for UTBILLPR. UTAGELF is an LF over UTBILL keyed by DUEDT (record UTAGER, exposing BILLNO/SPNO/DUEDT/CHARGE/BSTAT/BILLBAL) — the aging pass read by UTDELINQ, UTFEECAL and UTAGE via SETLL *LOVAL / forward READ.

UTGLF — GL posting feed (DB2 table)

FieldTypeMeaning
BATCHIDCHAR(8)Posting batch id (currently the static GLB00001).
ACCTCHAR(6)GL account: 100000 Cash, 120000 A/R, 400000 Revenue, 410000 Fee Revenue.
DRCRCHAR(1)D debit / C credit.
AMTDECIMAL(11,2)Posted amount.
SRCPGMCHAR(8)Source program (UTGLPST).

Relationships

E. Operations Runbook ↑ top

E.1 Day-in-the-life

  1. Confirm the build is in place (one-time, or after a rebuild): CALL AQUILA/UTSETUP creates every object, wires journaling and the ledger authority, and seeds the rate plan (DSPLY AQUILA setup complete. Rate plan loaded.).
  2. Maintain masters as needed through UTMNT (customers CC/CI, meters MC/MI).
  3. Load the day's meter reads into UTREAD (journaled) — in the test harness a small RPG loader writes them; in production this is the meter-read import.
  4. Run the night-batch chain (C.2) as sequenced SBMJOBs on AQUILA/NITEQ: BILLGEN → CASH POST → DELINQUENCY → FEE → GL POST → bill print.
  5. Handle online inquiries through CALL AQUILA/UTMENU as they arrive.
  6. When disconnected accounts pay up, run UTRECON to reconnect them.

Pre-checks: confirm the library list includes AQUILA; confirm the rate plan is present (UTRATE has 7 tiers); confirm each SBMJOB reached status = OUTQ before starting the next.

Post-checks per step (each program DSPLYs a one-line result):

E.2 The billing run & close

  1. Ensure the day's reads are loaded and BILLGEN/CASH/DELINQUENCY/FEE have all run to OUTQ.
  2. Run UTGLPST exactly once for the close. Confirm the joblog shows IN BALANCE … COMMITTED.
  3. Run the COBOL cross-check: CALL AQUILA/UTGLRPT. Confirm CROSS-CHECK: IN BALANCE.
  4. Review the GL feed and the aged-AR report.
-- GL control total (should balance and be non-zero)
SELECT COALESCE(SUM(CASE WHEN DRCR='D' THEN AMT ELSE 0 END),0),
       COALESCE(SUM(CASE WHEN DRCR='C' THEN AMT ELSE 0 END),0) FROM AQUILA.UTGLF;
-- A/R debit = billed charges + fees
SELECT COALESCE(SUM(AMT),0) FROM AQUILA.UTGLF WHERE ACCT='120000' AND DRCR='D';

Reconciling figures (the same ones the focused suites check):

Aged-AR: CALL AQUILA/UTAGE DSPLYs UTAGE BILLS=n TOTBAL=n OPEN=n DLQ=n for a quick open-vs-delinquent split.

E.3 Failure & re-run rules

Each program DSPLYs its result; the night chain sequences steps as separate SBMJOBs so a failed step can be re-submitted alone.

SituationBehaviourAction
BILLGEN re-runDeterministic BILLNO + CHAIN(EN) skips already-billed reads.Safe — no duplicate bills. Idempotent.
Oversized meter readCharge exceeds 9P2 capacity → clean reject, nothing written.Correct the read; REJECTED count flags it. No torn write.
CASH re-runApplies to the oldest open bill; a paid bill is no longer eligible.Re-run does not double-apply a settled bill; NOBILL rises if all bills are settled.
DELINQUENCY / FEE re-run same dayLEDGSEQ high-water-mark scan avoids a duplicate-key (SNX1021) on the second run; FEE's per-bill 'F'-row guard prevents fee-stacking within a pass.Safe to re-submit; fees are not double-assessed in the same pass.
GL POST run more than onceNot idempotent — re-posts the full cumulative total under the static BATCHID, inflating UTGLF.Run exactly once per close. If double-posted, the inflated rows must be cleared before re-closing; UTGLRPT flags it OUT OF BALANCE.
Reconnect while still owingUTRECON refuses (SPBAL > 0).Post the payment first, then re-run UTRECON.
Ledger SFF9802 authority errorBatch cannot write UTLEDG.Confirm *PUBLIC holds *CHANGE on UTLEDG (UTSETUP grants it); re-grant if a stricter authority was applied.
Because every money movement appends a keyed row to the journaled UTLEDG (B/P/A/F/R) and meter reads are journaled to UTREAD, any night's effect is fully reconstructable after the fact with DSPJRN for reconciliation and recovery. Use the single-paren DSPJRN … FILE(AQUILA/UTLEDG) form when filtering by file.

F. Developer Reference ↑ top

The complete program surface, from src/sources.mjs. All objects are in library AQUILA. RPG source is authored as column-exact fixed-form C/D-specs (helpers C()/D()/Ccond()) mixed with /free blocks; some programs are totally-free-form (**FREE).

F.1 Programs & languages

UTRATLD — RPG (fixed F-spec + /free)
Seeds the 7-tier rate plan into UTRATE (ELE×3, WAT×2, GAS×2), each top tier carrying the sentinel 999999999 upper bound and BASECHG on tier 1.
UTMNT — RPG (callable, dcl-pi *ENTRY)
Customer/meter create/change/inquire (CC/MC/CI/MI). Full-key CHAIN, validate-before-write, re-CHAIN before each UPDATE; reports via pOutFound/pOutMsg.
UTBLGEN — RPG (fixed C-specs + /free)
The tiered billing engine (F.2). Two-phase: compute-and-validate in wide working storage, then persist only a validated bill (no torn write).
UTCSHPST — RPG
Cash posting; forward-READ over UTBILL to find the oldest open bill per SPNO, partial (D) vs full (P), roll SPBAL down, ledger 'P'.
UTDELINQ — RPG
Aging pass over UTAGELF; flags bill/SP/customer delinquent, ledger 'A'. (Honest note: no grace period; per-bill — see C.2.)
UTFEECAL — RPG (packed-decimal calc)
Flat $5.00 + 1.50% interest on delinquent bills, wide-packed multiply then half-up to cents; per-bill 'F'-row idempotency guard; ledger 'F'.
UTRECON — RPG (two passes)
Pass 1 reconnects paid-up disconnected SPs; pass 2 restores a customer only if no other SP of theirs is still disconnected; ledger 'R'.
UTGLPST — SQLRPGLE (**FREE, commitment control)
Embedded-SQL SUM/INSERT into UTGLF; three balanced DR/CR pairs; COMMIT/ROLLBACK on the balance check.
UTBILLPR — RPG (PRTF)
Bill print over UTBILL + UTBLINL to the UTBILLP spool.
UTAGE — RPG
Aged-AR bucket report over UTAGELF.
UTACCTIQ / UTBILLIQ / UTMENU — RPG WORKSTN
Account inquiry (plain), bill inquiry (SFL/SFLCTL subfile), operator menu (indicator-conditioned fixed-form dynamic CALL to the two inquiries).
UTGLRPT — ILE COBOL
Indexed FD over UTBILL (zoned fields PIC S9(n), packed COMP-3); totals charges, adds ledger fees via embedded SQL, cross-checks the UTGLF A/R debit, prints IN BALANCE/OUT OF BALANCE.
UTSETUP / UTNIGHT — CL
Build/journal/authority/seed; night-batch chain documentation.

F.2 The tiered-billing algorithm (UTBLGEN)

For a read's usage U under rate class R, walk UTRATE tiers in TIERNO order (full composite-key CHAIN(E) on RATECD:TIERNO):

  1. Band width for tier t = TIERUB - prior TIERUB.
  2. Usage billed in the tier = MIN(remaining, band); line amount = usage × UNITPRC.
  3. On tier 1 add BASECHG; the sentinel-topped last tier absorbs any remaining usage.
  4. CHARGE = BASECHG + Σ(tier amounts).

Two-phase, torn-write-safe. Phase 1 computes the whole bill in wide 11P2 accumulators, writing nothing; if the computed charge (or any single tier amount) exceeds what the persistable 9P2 fields can hold, the read is rejected cleanly. Phase 2 runs only for a validated bill, re-walking the tiers deterministically to write the UTBLIN lines, then the UTBILL header, the SPBAL roll-up and the ledger 'B' row — so a detail line is never written for a read whose header would later fail. Packed math spans 9P0/7P4/9P2/11P2 widths.

F.3 Ledger key bands & idempotency

UTLEDG is a UNIQUE-keyed PF on LEDGSEQ (not a bare arrival file): a keyed WRITE appends, so the ledger accumulates across the whole night into a durable trail. Every posting program assigns LEDGSEQ from a disjoint numeric band so keys never collide:

ProgramLTYPELEDGSEQ bandAssignment
UTBLGENB10000000–10000000 + READID
UTCSHPSTP20000000–20000000 + RCPTNO
UTRECONR60000000–60999999high-water-mark + seq
UTFEECALF70000000–70999999high-water-mark + seq
UTDELINQA90000000–90999999high-water-mark + seq

The three high-water-mark programs (UTRECON/UTFEECAL/UTDELINQ) forward-scan their own band with a full-key SETLL to find the current max key before assigning, so a second run in the same business day that adds ≥1 new row does not restart at 0 and collide (which would raise SNX1021 duplicate-key). This is what makes those steps safely re-runnable.

F.4 Platform accommodations (honest)

Documented in the source and PLAN.md; operationally the app behaves as described, but these are the real reasons certain names and idioms look the way they do:

G. Glossary ↑ top

Arrival file
A physical file read in the order rows were added (via SETLL *START / forward READ). Here UTREAD and UTCASH are read in arrival order for their one-pass batch programs (they are UNIQUE-keyed for the write path but read sequentially).
Base charge (BASECHG)
The fixed monthly service charge for a rate class, added on tier 1 of a bill regardless of usage.
BILLNO scheme
A bill's key = 'B' + last 7 digits of (READID + 10000000), making it deterministic per read (READID 1001 → B0001001) so re-running the billing engine never duplicates a bill.
Commitment control
The transaction scope under which UTGLPST posts to UTGLF; it COMMITs only if the batch balances, else ROLLBACK. Honored on the SQL path, so GL posting is done entirely in embedded SQL.
Delinquency bucket / aging
Classifying open bills by their due date. UTAGELF keys UTBILL by DUEDT for the aging pass; UTAGE splits balances into open vs delinquent.
Disconnect / reconnect
Setting a service point SPSTAT='D' when its bill is delinquent (UTDELINQ) and back to 'A' once paid up (UTRECON) — the signature utility service-lifecycle arc.
Idempotent
Safe to run again with the same result. UTBLGEN and UTFEECAL are idempotent within a pass; the high-water-mark posting steps are re-run safe on the ledger key; UTGLPST is not (it re-posts cumulative totals).
Journaling
Recording before/after images of file changes to a journal receiver. UTREAD and UTLEDG are journaled with IMAGES(*BOTH) to UTJRN/UTRCV, inspectable with DSPJRN/WRKJRNA.
Ledger (UTLEDG) / LTYPE
The immutable A/R audit trail. LTYPE tags the event: B bill, P payment, A adjustment (delinquency), F fee, R reconnect.
LEDGSEQ band
The disjoint numeric range each posting program draws its ledger key from (F.3), so keys never collide across the night.
Packed decimal (nP d)
A compact numeric storage format (two digits per byte). AQUILA's billing and fee math uses wide packed accumulators (up to 15P4) so multiplies never overflow before rounding to cents.
PRTF / spool
A printer file and the spooled output it produces. UTBILLP is the bill printer file; UTBILLPR writes the spool.
Rate class / tier (UTRATE)
A utility's stepped price schedule (ELE/WAT/GAS), keyed by RATECD + TIERNO; each tier has an upper bound and a unit price, with the top tier sentinel-topped to absorb the rest.
SBMJOB / *JOBQ
Submit Job to a job queue — how the night-batch steps are sequenced so each program closes its files before the next opens them.
Service point (UTSVCPT)
A metered point of service for a customer under one utility type and rate class, carrying its own status and running balance.
Subfile (SFL/SFLCTL)
A 5250 construct listing many rows on one screen. UTBILLIQ's BSFL lists a bill's tier lines under control record BCTL.
Tiered (stepped) billing
Billing each band of usage at its own unit price (F.2), plus a base charge, rather than a single flat rate.