HAULPRO/i is a solid-waste hauling and disposal back-office application: customer,
service-location, container and route masters; recurring service schedules (the contract); a daily
route run that creates service tickets for schedules due that day and logs the disposal/landfill
tonnage tickets the hauler pays the tipping fee on; a monthly recurring billing run (contracted base
rate × pickups + extra/unscheduled pickups + a fuel/environmental surcharge) into a customer
invoice and AR ledger; and a period close that ages the ledger, assesses late fees, and posts revenue,
disposal cost, late-fee income and AR to a GL feed. The commercial logic lives in fixed-form and free-form
RPG (with one ILE COBOL reconciliation report and one SQLRPGLE GL poster); the three job cycles are CL
drivers. This manual is the reference for the operator who runs the online screens and the daily / monthly
/ period cycles, and for the developer maintaining the application. It is grounded entirely in the
committed source (waste-app/src/sources.mjs, src/seed.mjs, and the
test/hp_build.mjs / hp_daily.mjs / hp_cycles.mjs drivers).
HAULPRO/i runs the back office of a commercial waste hauler:
The system's spine is a single chain:
Service schedule (recurring contract: location + container + frequency + base rate) → the daily route run creates service tickets for schedules due that day and logs the day's disposal tonnage/tipping-fee tickets → the monthly billing run sums a customer's tickets for the month (base rate × scheduled pickups, plus extra pickups, plus surcharge) into one invoice → posts to the customer ledger (AR) → the period close ages the ledger, assesses late fees, and posts the period's activity to the GL.
The invariant asserted throughout the tests: revenue side and cost side never mix on one row. A service ticket's route pickup is billed to the customer; a disposal ticket's landfill tipping fee is the hauler's own expense, tracked and GL-posted separately, never itemised to any customer. A customer's monthly invoice amount is exactly:
IAMT = (schedule base rate x scheduled pickups this period) = IBASE + (extra/unscheduled pickup count x extra-pickup rate) = IEXTRA + ((IBASE + IEXTRA) x customer fuel/environmental pct) = ISURCH
so the GL and the customer ledger always foot to the same billed amount, split three ways (base service revenue / extra-pickup revenue / surcharge revenue), with disposal cost tracked separately.
SETUP / REFERENCE ONLINE BATCH (periodic)
----------------- ------ ----------------
HLSETUP (CL) HLMENU (5250) HLDAILY ---> HLPICKUP (service tickets)
build all objects opt 1 -> HLCUSTIQ HLDISPRUN (disposal tickets)
HLREFLD (RPG) opt 2 -> HLLEDIQ HLMONTH ---> HLBILL (invoice + AR ledger)
seed masters (subfile) HLPRICL ---> HLAGE (aging -> late fees)
HLARRP (COBOL AR reconcile)
\ / HLGLPST (GL close)
\ /
v v
HLSKED (the contract) -> HLTICK -> HLBILL -> HLINVH/HLINVL + HLLEDG (AR)
HLDISP -------------> HLGLFEED (GL) HLAGE -> HLLEDG ('F')
A single daily event flows: operator runs HLDAILY → HLPICKUP walks every
active HLSKED row and writes one HLTICK (TKTYPE='S') per schedule →
HLDISPRUN sums each active route's tickets for the day and writes one HLDISP
tonnage/tipping-fee row. Monthly, HLBILL turns unbilled tickets into HLINVH/
HLINVL invoices and an HLLEDG 'I' row; the period close ages and posts to the
HLGLFEED GL table.
| Object | Type | Role |
|---|---|---|
| HLCUST | PF | Customer master (surcharge pct, terms, AR balance). |
| HLLOC | PF | Service-location master. |
| HLCONT | PF | Container/dumpster master (size, type). |
| HLROUTE | PF | Route / truck / driver master. |
| HLSKED | PF | Recurring service schedule (the contract). |
| HLTICK | PF | Service ticket (one per route pickup). |
| HLTKLF | LF | Tickets by billed-status / location (billing path). |
| HLDISP | PF | Disposal/landfill tonnage ticket (hauler cost). |
| HLLEDG | PF | Customer AR ledger (signed I/P/F rows). |
| HLLEDCF | LF | Ledger by customer (aging / inquiry path). |
| HLINVH / HLINVL | PF | Invoice header / itemised lines. |
| HLINVLF | LF | Invoices by status (AR-aging path). |
| HLGLFEED | SQL table | GL feed the period close posts into. |
| HLCUSTD / HLLEDD / HLMENUD | DSPF | Inquiry, ledger-subfile, menu screens. |
| HLINVP | PRTF | Customer invoice statement printer file. |
| HLREFLD | RPGLE | Seed reference/master data. |
| HLPICKUP | RPGLE | Daily: create scheduled service tickets. |
| HLDISPRUN | RPGLE | Daily: log disposal tonnage tickets. |
| HLBILL | RPGLE | Monthly: recurring service billing. |
| HLAGE | RPGLE | Period: AR aging → late fees. |
| HLGLPST | SQLRPGLE | Period: GL close (embedded SQL). |
| HLARRP | CBLLE | Period: COBOL AR reconciliation report. |
| HLCUSTIQ / HLLEDIQ / HLMENU | RPGLE | Online inquiry / ledger / menu drivers. |
| HLSETUP | CLP | Create every object & compile every program. |
| HLDAILY / HLMONTH / HLPRICL | CLP | The three job-cycle drivers. |
The catalogue is 10 PFs, 3 LFs, 3 DSPFs, 1 PRTF and 1 SQL table, driven by 9 RPG programs, 1 ILE COBOL
program and 4 CL programs. All objects live in library HAULPRO. Sections D and F expand each.
HAULPRO/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
cycles). 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 HAULPRO — the
tested jobs run with LIBL = QSYS QGPL HAULPRO QTEMP and CURLIB = HAULPRO. The CL
cycle drivers ADDLIBLE LIB(HAULPRO) themselves, so a bare CALL of a cycle is
self-sufficient.
| To do this | Type on the command line |
|---|---|
| Open the main menu (routes to the two inquiries) | CALL HAULPRO/HLMENU |
| Open customer inquiry directly | CALL HAULPRO/HLCUSTIQ |
| Open the customer ledger (subfile) inquiry directly | CALL HAULPRO/HLLEDIQ |
| Build every object & compile every program (first time) | CALL HAULPRO/HLSETUP |
| Seed the reference/master data | CALL HAULPRO/HLREFLD |
| Run the daily route/disposal cycle | CALL HAULPRO/HLDAILY (or SBMJOB it) |
| Run the monthly billing cycle | CALL HAULPRO/HLMONTH |
| Run the period AR-aging / GL-close cycle | CALL HAULPRO/HLPRICL |
The batch programs take no CALL parameters. In this build each program's processing date /
period is a literal set in working storage (e.g. wtoday = 20260915; in HLPICKUP,
wperiod = 20260930; in HLBILL, wasof = 20261101; in HLAGE); a production build
would read those from a control row or the system date. The daily and period cycle drivers chain more than
one program (see section C); only the three inquiry programs are interactive.
HAULPRO/i has three interactive programs, reached from the menu HLMENU (DDS
HLMENUD): option 1 calls HLCUSTIQ, option 2 calls
HLLEDIQ; F3 exits. Both inquiries loop on their own screen until
F3.
A non-subfile screen: the operator keys a customer number and HLCUSTIQ CHAINs
HLCUST, rendering the billing detail, a derived status description, and the current AR
balance. Not found leaves a "Customer not found." message.
| Field (DDS) | Shows |
|---|---|
| ICUST | Input customer number (6A). |
| DCNAME / DBILTO | Customer name and bill-to (from CNAME / CBILTO). |
| DFSC / DTERM | Fuel/enviro surcharge pct and payment terms (%editc-formatted CFSCPCT / CPAYTRM). |
| DSTATD | Derived status: ACTIVE (A) / DELINQUENT (D) / CLOSED (C) / UNKNOWN. |
| DBAL | Current AR balance (CBAL, edited). |
The app's subfile screen. HLLEDD defines a subfile record LSFL under control
record LCTL with SFLPAG(5) / SFLSIZ(20) and
ROLLUP/ROLLDOWN so paging is exercised. The operator keys a customer number;
HLLEDIQ displays the screen, clears then reloads the subfile from
HLLEDCF (the by-customer ledger access path), one row per ledger line, and shows a line count
and running balance.
| Subfile field | Shows |
|---|---|
| TSEQ | Ledger sequence (LEDGSEQ). |
| TTYPE | Ledger type: I invoice, P payment, F late fee. |
| TDT / TAMT | Ledger date / signed amount (edited from LDT / LAMT). |
| TMEMO | Free-text memo (e.g. INVOICE CU000101, FEE CU000101). |
| DLCNT / DLBAL | Control-line line count and running balance. |
EXFMT LCTL), then set on indicator 31 and WRITE LCTL to clear (SFLCLR), then
reload from the keyed access path — so a re-inquiry against a different customer shows fresh rows,
not a stale load. The conditioning indicators in HLLEDD are written one column left of the
standard DDS position to satisfy the emulator's DDS parser offsets (documented platform quirk HL-PLAT-001,
cross-referencing FR-PLAT-001); operationally the screen behaves exactly as described.Honest statement: HAULPRO/i does not model a true four-eyes maker–checker / separate-authorization workflow. There is no "one user posts, a second user approves" step: the batch cycles post invoices, fees and GL rows directly, and the two online programs are read-only inquiries (they only CHAIN/READ masters and the ledger — no operator can key a maintenance action against a customer or invoice through the screens). The manual documents the control model the application does have:
HLARRP re-sums these rows
independently and proves HLCUST.CBAL is reconstructible from the detail alone.LEDGSEQ band — HLBILL in 10000000–10999999, HLAGE
in 30000000–30999999 — discovered by a forward high-water-mark scan, so two programs
(or a re-run for a different period) never collide on the shared ledger file.HLBILL only bills
active customers (CSTAT='A') and only unbilled tickets (TBILLED='N');
HLAGE only ages open invoices (ISTAT='O') whose IDUEDT is
before the as-of date; the daily programs only act on active schedules / routes.HLAGE finds a past-due invoice it flips the invoice to
ISTAT='D' and the owning customer to CSTAT='D' (the utilities-app
bill→service-point→customer delinquency cascade), so the customer inquiry immediately
renders DELINQUENT.TICKNO, an invoice already carrying
this period's derived INVNO, a ticket already flipped to TBILLED='Y', an
invoice already fee-tagged in the ledger, or a GL row whose GLREF is already present are
all skipped.In sum, the control posture is signed-ledger auditability + status/state gating + idempotency enforcement, plus an independent COBOL reconciliation, rather than a segregation-of-duties approval workflow.
HAULPRO/i's work runs as three CL-driven cycles rather than one monolithic nightly job: a daily
cycle (route pickups + disposal log), a monthly cycle (recurring service billing), and a
period cycle (AR aging → late fees, COBOL reconciliation, and the GL close). Each cycle is a CL
program that ADDLIBLE LIB(HAULPRO) then CALLs its RPG/COBOL/SQLRPGLE steps in
order; a scheduled/JOBQ submission is a bare CALL or SBMJOB.
-- the three cycles, submittable bare (each ADDLIBLEs HAULPRO itself)
SBMJOB CMD(CALL PGM(HAULPRO/HLDAILY)) JOB(HLDAILY)
SBMJOB CMD(CALL PGM(HAULPRO/HLMONTH)) JOB(HLMONTH)
SBMJOB CMD(CALL PGM(HAULPRO/HLPRICL)) JOB(HLPRICL)
| Program | Cycle | Purpose | Reads | Writes / DSPLY |
|---|---|---|---|---|
| HLREFLD | Setup | Seed masters: customers, locations, containers, routes, schedules. | — (literals). | HLCUST/HLLOC/HLCONT/HLROUTE/HLSKED; DSPLY HLREFLD CUSTOMERS=2 LOCATIONS=2 CONTAINERS=2 ROUTES=2 SCHEDULES=2. |
| HLPICKUP | Daily (1) | Create one scheduled service ticket per active schedule. | HLSKED, HLCONT. | HLTICK (TKTYPE='S'); DSPLY HLPICKUP CREATED=n SKIP=n. |
| HLDISPRUN | Daily (2) | Log one disposal tonnage/tipping-fee ticket per active route that had pickups. | HLROUTE, HLTICK/HLTKLF. | HLDISP; DSPLY HLDISPRUN LOGGED=n SKIP=n. |
| HLBILL | Monthly | Recurring service billing: base + extra + surcharge → invoice + AR ledger. | HLCUST, HLLOC, HLSKED, HLTICK/HLTKLF. | HLINVH/HLINVL, HLLEDG ('I'), rolls CBAL; DSPLY HLBILL BILLED=n SKIP=n. |
| HLAGE | Period (1) | AR aging: flag past-due invoices/customers delinquent, assess late fees. | HLINVH/HLINVLF, HLCUST, HLLEDG. | ISTAT→D, CSTAT→D, HLLEDG ('F'), rolls CBAL; DSPLY HLAGE FLAGGED=n ASSESSED=n. |
| HLARRP | Period (2) | COBOL: re-sum the ledger independently to reconcile CBAL. | HLLEDG (sequential). | DSPLY HLARRP INVOICED/LATEFEE/PAYMENTS/NET/LINES. |
| HLGLPST | Period (3) | GL close: post revenue, late-fee income, disposal-cost pairs. | HLLEDG, HLDISP, HLGLFEED (embedded SQL). | HLGLFEED rows; DSPLY HLGLPST BATCH=n ROWS=n. |
HLPICKUP walks every active schedule (SKSTAT='A') and creates one scheduled ticket
(TKTYPE='S') dated today. It derives the pickup weight deterministically from the container's
size — TWEIGHT = CSIZE × 0.85 ton/yard (the fixed simplification in place of a
live truck-scale feed) — and derives TICKNO = SKEDNO + last-2-digits-of-WTODAY so a
re-run for the same day is idempotent (a CHAIN(EN) guard refuses the second attempt).
HLDISPRUN runs second, so today's tickets already exist. For each active route
(RSTAT='A') it sums that route's tickets for today, looks up the route's per-ton tipping fee
from a fixed table (RT0001 $45.00, RT0002 $50.00, any other route
$40.00), and writes one disposal ticket DTAMT = DTWEIGHT × DTPFEE. A route
with no pickups today writes nothing (idempotent-by-construction). DISPNO is derived the same
way as TICKNO, so a same-day re-run logs nothing further.
Expected DSPLY (2 seeded schedules, one 6-yard on RT0001, one 8-yard on RT0002): HLPICKUP CREATED=2 SKIP=0 SK0001->5.10t, SK0002->6.80t HLDISPRUN LOGGED=2 SKIP=0 RT0001 5.10x45=229.50, RT0002 6.80x50=340.00 Re-run the same day: HLPICKUP CREATED=0 SKIP=2 HLDISPRUN LOGGED=0 SKIP=2
HLBILL, for each active customer, walks the unbilled bucket of HLTKLF
(TBILLED='N'), keeping only tickets at that customer's own service locations (a
CHAIN to HLLOC checks CUSTNO). It splits scheduled
(TKTYPE='S') from extra (TKTYPE='X') pickups:
SKRATE (a customer with two schedules at two rates bills each at its own rate).$95.00.(BASE + EXTRA) × CFSCPCT / 100, the customer's contracted
fuel/environmental surcharge.Each ticket walked is flipped to TBILLED='Y' (which moves it out of the N bucket), so a
re-run bills nothing — idempotent by construction. A customer with no unbilled tickets contributes
nothing (no zero-dollar invoice). The invoice header (with three line rows: BASE, EXTRA if any, SURCHARGE)
is written, an 'I' ledger row posted, CBAL rolled forward, and the due date computed as
period + CPAYTRM (a numeric add on the YYYYMMDD-shaped integer, not a calendar rollover).
Expected DSPLY (2 customers; CU0001 has 1 scheduled + 1 planted extra, CU0002 1 scheduled): HLBILL BILLED=2 SKIP=0 CU0001: BASE 180.00 + EXTRA 95.00 + SURCH 22.00 = 297.00 (FSC 8.00%, due 20260960) CU0002: BASE 220.00 + EXTRA 0.00 + SURCH 13.20 = 233.20 (FSC 6.00%, due 20260945) Re-run same period: HLBILL BILLED=0 SKIP=2
HLAGE makes one forward pass over open invoices (ISTAT='O' via
HLINVLF). Any invoice whose IDUEDT is before the as-of date is past due: flip its
ISTAT to 'D', flip the owning customer's CSTAT to 'D', and assess a late fee of
$25.00 + 1.5% monthly interest on IAMT (a wide packed multiply, half-up biased with
+0.005 then truncated to cents), posting an 'F' ledger row and rolling CBAL
forward. An idempotency guard scans HLLEDG for an existing 'F' row already carrying this
invoice's memo tag before assessing again, so a re-run assesses nothing.
HLARRP (COBOL) reads the ledger sequentially and sums 'I', 'F' and 'P' rows into invoiced, late-fee and payment totals plus a net — an independent reconstruction of what the customers owe.
HLGLPST (SQLRPGLE) posts three balanced GL pairs, each driven off rows not yet posted (a
GLREF-already-present check via embedded SQL makes the whole close idempotent without a
posted-flag column): revenue per 'I' ledger row (DR 1200-ARV / CR 4000-REV), late-fee income
per 'F' row (DR 1200-ARV / CR 4800-FEE), and disposal cost per disposal ticket (DR 5000-DISP / CR
2000-APV).
Expected DSPLY (as-of 20261101; both invoices past due): HLAGE FLAGGED=2 ASSESSED=2 CU0001 fee 29.46, CU0002 fee 28.50 HLARRP INVOICED 530.20 LATEFEE 57.96 PAYMENTS 0.00 NET 588.16 LINES 4 HLGLPST BATCH=202610 ROWS=12 SUM(DR)=SUM(CR)=1157.66 Re-run: HLAGE FLAGGED=0 ASSESSED=0 HLGLPST ... ROWS=0
All files are in library HAULPRO, grounded in the DDS/SQL in src/sources.mjs.
Dates are stored as signed numeric in YYYYMMDD form; money and rates are packed
P decimal; the surcharge percentage is a packed 5P2 (8.00 = 8%).
| Field | Type | Meaning |
|---|---|---|
| CUSTNO | 6A | Customer number (key). |
| CNAME / CBILTO | 30A / 30A | Customer name / bill-to name. |
| CFSCPCT | 5P 2 | Fuel/environmental surcharge pct (contract term, per customer). |
| CPAYTRM | 3S 0 | Net payment terms, days. |
| CBAL | 11P 2 | Current AR balance (rolled by billing & aging). |
| CSTAT | 1A | A active, D delinquent (set by aging), C closed. |
| Field | Type | Meaning |
|---|---|---|
| SLOC | 6A | Service-location number (key). |
| CUSTNO | 6A | Owning customer. |
| SADDR / SCITY | 30A / 20A | Street address / city. |
| SSTAT | 1A | Location status. |
| Field | Type | Meaning |
|---|---|---|
| CONTNO | 6A | Container number (key). |
| CSIZE | 3S 0 | Yard capacity (2/4/6/8/20/30/40) — drives the derived pickup weight. |
| CTYPE | 1A | F front-load, R roll-off. |
| CTLOC | 6A | Service location the container currently sits at. |
| CTSTAT | 1A | Container status. |
| Field | Type | Meaning |
|---|---|---|
| RTENO | 6A | Route number (key). |
| RTDESC | 20A | Route description. |
| TRUCKNO / DRVNO | 6A / 6A | Truck / driver on the route. |
| RSTAT | 1A | A active, H on hold, T terminated. |
| Field | Type | Meaning |
|---|---|---|
| SKEDNO | 6A | Schedule number (key). |
| SLOC / CONTNO / RTENO | 6A each | Location, container and route this contract serves. |
| SKFREQ | 1A | W weekly, X biweekly (twice weekly), M monthly — drives which day a ticket is created, not the billed amount. |
| SKRATE | 9P 2 | Contracted base rate per pickup. |
| SKSTAT | 1A | A active, S suspended. |
| Field | Type | Meaning |
|---|---|---|
| TICKNO | 8A | Ticket number (key), derived SKEDNO + day-of-month for scheduled tickets. |
| SKEDNO / SLOC / RTENO | 6A each | Schedule, location, route (carried on the ticket directly). |
| TDATE | 8S 0 | Pickup date (YYYYMMDD). |
| TKTYPE | 1A | S scheduled, X extra/unscheduled. |
| TWEIGHT | 7P 2 | Actual tonnage picked up. |
| TBILLED | 1A | N not yet billed, Y billed (flipped by HLBILL). |
HLTKLF — logical file over HLTICK keyed TBILLED, SLOC, TICKNO
(non-unique): the by-billed-status access path HLBILL walks so it need not scan HLTICK by ticket number.
| Field | Type | Meaning |
|---|---|---|
| DISPNO | 8A | Disposal ticket number (key), derived RTENO + day-of-month. |
| RTENO | 6A | Route that dumped this load. |
| DDATE | 8S 0 | Disposal date (YYYYMMDD). |
| DTWEIGHT | 9P 2 | Total tonnage hauled to the landfill that day. |
| DTPFEE | 7P 2 | Tipping-fee rate per ton at the route's landfill. |
| DTAMT | 11P 2 | Hauler's disposal cost = DTWEIGHT × DTPFEE (never billed to a customer). |
| Field | Type | Meaning |
|---|---|---|
| LEDGSEQ | 8S 0 | Global ledger sequence (key); reserved key bands per writer (10M / 30M). |
| CUSTNO | 6A | Owning customer. |
| LTYPE | 1A | I invoice (+), P payment (−), F late fee (+). |
| LDT | 8S 0 | Ledger date (YYYYMMDD). |
| LAMT | 11P 2 | Signed amount. |
| LMEMO | 25A | Free-text memo (e.g. INVOICE CU000101, FEE CU000101). |
HLLEDCF — logical file over HLLEDG keyed CUSTNO, LEDGSEQ
(non-unique): the by-customer AR path HLAGE and HLLEDIQ walk.
| Field | Type | Meaning |
|---|---|---|
| INVNO | 8A | Invoice number (key), derived from customer + period sequence. |
| CUSTNO | 6A | Billed customer. |
| IDATE / IDUEDT | 8S 0 | Invoice date / due date (= period + CPAYTRM, numeric add). |
| IBASE / IEXTRA / ISURCH | 11P 2 | Base-service / extra-pickup / surcharge amounts. |
| IAMT | 11P 2 | Invoice total (foots to IBASE+IEXTRA+ISURCH). |
| ISTAT | 1A | O open, P paid, D delinquent (set by aging). |
| Field | Type | Meaning |
|---|---|---|
| INVNO / ILSEQ | 8A / 3S 0 | Invoice + line sequence (key). |
| ITYPE | 1A | B base service, X extra pickups, F fuel/enviro surcharge. |
| IDESC | 25A | Line description. |
| IAMTL | 11P 2 | Line amount (the three lines foot to HLINVH.IAMT). |
HLINVLF — logical file over HLINVH keyed ISTAT, CUSTNO, INVNO
(non-unique): the by-status AR-aging path HLAGE walks.
| Column | Type | Meaning |
|---|---|---|
| GLSEQ | DECIMAL(8,0) | GL sequence (PK). |
| GLBATCH | DECIMAL(6,0) | Posting batch (e.g. 202610). |
| ACCT | CHAR(9) | GL account (1200-ARV / 4000-REV / 4800-FEE / 5000-DISP / 2000-APV). |
| DRCR | CHAR(1) | D debit, C credit. |
| AMT | DECIMAL(11,2) | Posting amount. |
| GLREF | CHAR(8) | Source key (ledger seq or disposal ticket no) — the idempotency guard. |
| GLDT | DECIMAL(8,0) | Posting date (YYYYMMDD). |
Index HLGLFACC on (ACCT, DRCR). The by-GLREF duplicate check drives idempotency.
First-time / rebuild setup (idempotent — HLSETUP DLTFs then re-creates every
file):
CALL HAULPRO/HLSETUP — creates every PF/LF/DSPF/PRTF, runs the two SQL DDL members
(HLGLFD/HLGLFC), and compiles every program; confirm the "HAULPRO setup complete." banner.CALL HAULPRO/HLREFLD — seeds 2 customers, 2 locations, 2 containers, 2 routes, 2
schedules; confirm HLREFLD CUSTOMERS=2 ... SCHEDULES=2.Daily operation:
SBMJOB CMD(CALL PGM(HAULPRO/HLDAILY)).CALL HAULPRO/HLMENU (options 1 and 2) as
needed.Pre-checks: confirm the job's library list includes HAULPRO (the cycle drivers
ADDLIBLE it themselves); confirm the reference data has been seeded (HLREFLD).
Post-checks after the daily cycle:
HLPICKUP CREATED= equals the count of active schedules; one TKTYPE='S'
ticket per schedule, dated today, TBILLED='N'.HLDISPRUN LOGGED= equals the count of active routes that had pickups today; each
disposal ticket's DTAMT = DTWEIGHT × DTPFEE at the route's fixed tipping rate.CREATED=0 / LOGGED=0 and adds no rows (the
derived-key idempotency guard).TBILLED='N').SBMJOB CMD(CALL PGM(HAULPRO/HLMONTH)). Confirm
HLBILL BILLED= equals the active customers with unbilled activity, and each invoice
foots IAMT = IBASE + IEXTRA + ISURCH.SBMJOB CMD(CALL PGM(HAULPRO/HLPRICL)) — runs HLAGE,
HLARRP, HLGLPST in that order.Reconciling figures (the same ones the cycle simulation checks against a hand-derived oracle):
IAMT = base(Σ own SKRATE per scheduled
pickup) + extra(count × $95.00) + surcharge((base+extra) × CFSCPCT/100). The three
HLINVL lines foot to the header.HLCUST.CBAL = Σ signed HLLEDG rows for the customer; the
COBOL HLARRP re-derives INVOICED / LATEFEE / PAYMENTS / NET from the ledger detail
alone (proving CBAL is reconstructible).ISTAT='D', their customers
CSTAT='D', each with one 'F' ledger row (fee = $25.00 + 1.5% × IAMT, biased then
truncated to cents).SUM(DR) = SUM(CR) across HLGLFEED; revenue on 4000-REV, late-fee
income on 4800-FEE, disposal cost on 5000-DISP, with 1200-ARV the shared AR debit and 2000-APV the
landfill payable.Ad-hoc GL review: SELECT ACCT, DRCR, SUM(AMT) FROM HAULPRO.HLGLFEED GROUP BY ACCT, DRCR
ORDER BY ACCT, DRCR; gives the trial-balance-style rollup (using the HLGLFACC index).
Each program DSPLYs a count summary (CREATED/LOGGED/BILLED/FLAGGED/ASSESSED/ROWS). Every
cycle is safe to re-run because each program is idempotent by a derived key or a status flip.
| Situation | Behaviour | Action |
|---|---|---|
| Daily cycle fails partway | Some schedules ticketed, some not; disposal only sees the ones ticketed. | Re-submit HLDAILY for the same day: already-ticketed schedules skip (CREATED counts only the new ones), then HLDISPRUN re-sums. Idempotent by derived TICKNO/DISPNO. |
| Re-run the same day's daily cycle | CREATED=0 LOGGED=0; no new rows. | Safe no-op. Only a different day (different WTODAY) creates new tickets. |
| Monthly billing re-run same period | BILLED=0; every ticket already TBILLED='Y', plus the INVNO CHAIN(EN) belt-and-suspenders guard. | Safe no-op. Idempotent by construction. |
| Period aging re-run same as-of | FLAGGED=0 ASSESSED=0; invoices no longer ISTAT='O' and each already has its 'F' fee-tag in the ledger. | Safe no-op. Do not change the as-of literal to re-age the same invoices. |
| GL close re-run same period | ROWS=0; every GLREF already present in HLGLFEED. | Naturally idempotent — the by-GLREF SELECT excludes already-posted sources. |
| A route with no pickups today | No disposal ticket written (SKIP incremented). | Expected — "nothing due, nothing happens". Not an error. |
| Ledger key-band exhaustion | Writer's reserved band (e.g. 10M/30M) full. | Design headroom is 1,000,000 rows per band per program; widen the band before it fills (out of scope for this build's volumes). |
HLARRP reconciliation is the built-in proof that CBAL equals the ledger detail.The program surface, from src/sources.mjs. All objects are in library
HAULPRO. The RPG programs are column-exact fixed-form with /free blocks;
HLGLPST is SQLRPGLE (embedded SQL); HLARRP is ILE COBOL; the cycles are CL.
SKSTAT≠'A'); captures SKEDNO/SLOC/RTENO/
CONTNO into work fields before CHAINing HLCONT (shared external field names); derives
TWEIGHT = CSIZE × 0.85 and TICKNO = WSKED + %subst(%char(WTODAY):7:2);
CHAIN(EN) guard on TICKNO; WRITEs HLTICK with TKTYPE='S', TBILLED='N'.RTENO=WRTE and TDATE=WTODAY
to sum weight; fixed tipping table (RT0001 45 / RT0002 50 / else 40); writes HLDISP with
DTAMT = DTWEIGHT × DTPFEE; CHAIN(EN) guard on the derived DISPNO.TBILLED='N' bucket of HLTKLF, CHAINing HLLOC to confirm the ticket
belongs to this customer, summing BASE (each ticket's own SKRATE via HLSKED CHAIN), counting EXTRA;
flips each walked ticket to TBILLED='Y'; computes SURCH and total; WRITEs HLINVH + 2–3
HLINVL lines + an 'I' HLLEDG row; rolls CBAL. INVNO CHAIN(EN) guard.$25.00 + IAMT × 0.0150 (wide packed multiply, +0.005
bias, truncate to cents); scans HLLEDG for an existing 'F' + matching LMEMO tag to avoid
double-assessing; WRITEs an 'F' HLLEDG row; rolls CBAL.EXEC SQL SELECT COUNT(*) before posting a DR/CR pair through the glrow
subroutine (INSERT with MAX(GLSEQ)+1).HAULPRO/i has no separate control table; idempotency is achieved two ways, mirroring the estate's siblings:
TICKNO = SKEDNO + day (HLPICKUP),
DISPNO = RTENO + day (HLDISPRUN), INVNO = CUSTNO + period-seq (HLBILL):
a re-run for the same day/period derives the same key, the CHAIN(EN) finds it, and the write is
refused (freight-app FRRATE convention).TBILLED N→Y (moving the ticket
out of the scanned bucket) and HLAGE flips ISTAT O→D, so a re-run's selection finds
nothing (freight-app FRDISP/FRPOD convention). HLGLPST excludes GLREFs already in HLGLFEED
(FRGLPST/WHGLPST convention).| Posting | Debit | Credit | Driven off |
|---|---|---|---|
| Service revenue | 1200-ARV | 4000-REV | each HLLEDG 'I' row (base+extra+surcharge) |
| Late-fee income | 1200-ARV | 4800-FEE | each HLLEDG 'F' row |
| Disposal cost | 5000-DISP | 2000-APV | each HLDISP row (tipping fee, hauler expense) |
Each source produces exactly one balanced DR/CR pair (two rows). 1200-ARV is the shared AR
debit for revenue and fees; 2000-APV is the payable to the landfill. GLREF carries the source
key (ledger seq or disposal ticket no) so the close is idempotent without a posted-flag column.
HLLEDD writes its
SFLDSP/SFLDSPCTL/SFLCLR conditioning indicators one column left of the standard DDS position so the
emulator's DDS parser reads them correctly (cross-references FR-PLAT-001; confirmed platform issue,
not re-derived). The screen behaves normally at runtime.IDUEDT = period + CPAYTRM is a
numeric add, not a calendar rollover (so 20260930 + 30 = 20260960), the freight-app FRRATE
convention — the tests assert exactly these numeric values.+0.005 bias then packs
(truncates) to cents, giving deterministic figures the tests hand-derive.The hp_daily.mjs / hp_cycles.mjs scenario, hand-derived end to end from the
seed data (2 customers, weekly schedules SK0001 @180.00 / SK0002 @220.00, plus one planted extra ticket at
CU0001):
DAILY (WTODAY 20260915) SK0001 6yd x 0.85 = 5.10t RT0001 x $45/t = $229.50 disposal SK0002 8yd x 0.85 = 6.80t RT0002 x $50/t = $340.00 disposal MONTHLY (period 20260930) CU0001 BASE 180.00 + EXTRA 95.00 + SURCH 22.00 (8%) = IAMT 297.00 due 20260960 CU0002 BASE 220.00 + EXTRA 0.00 + SURCH 13.20 (6%) = IAMT 233.20 due 20260945 PERIOD (as-of 20261101) CU0001 fee = 25.00 + 297.00 x 1.5% (4.455->4.46) = 29.46 CBAL 326.46 CU0002 fee = 25.00 + 233.20 x 1.5% (3.498->3.50) = 28.50 CBAL 261.70 HLARRP INVOICED 530.20 LATEFEE 57.96 PAYMENTS 0.00 NET 588.16 LINES 4 GL REV 530.20 FEE 57.96 DISP 569.50 12 rows SUM(DR)=SUM(CR)=1157.66
DTWEIGHT ×
DTPFEE). A pure cost, never billed to any customer — the hauler's own expense.SBMJOB CMD(CALL PGM(HAULPRO/HLDAILY))).