SIMPINV/i is a compact inventory and order-posting application: it seeds an item master with
reference data, posts a stream of receipt / issue / removal transactions against that master, and prints a
class-summary report driven by the classic single-primary RPG cycle. It is written entirely in classic
RPG III / OPM RPG/400 fixed-form source (H/F/I/E/C/O specifications, compile-time arrays, the RPG
cycle) and compiled through CRTRPGPGM — SteelFrame X's rpg3.js front end that
converts RPG III to RPG IV (via the CVTRPGSRC pipeline). This manual is honest about what it is: a
language-reference / demo estate, deliberately built to exercise classic RPG III features against
real business data, not a large production system. It is grounded entirely in the committed source
(rpg3-app/src/sources.mjs, rpg_invseed.mjs, rpg_invpost.mjs,
rpg_invclsr.mjs, seed.mjs, and the test/inv_build.mjs driver).
SIMPINV/i runs a three-step inventory flow, one RPG III program per step, all in library
SIMPINV:
INVSEED) — loads five items across three item
classes (RAW, FIN finished goods, SUP supplies) into INVMAS, stamping
each with an on-hand quantity, unit price and class code, and emitting a DSPLY trace of the load.INVPOST) — reads every transaction from
INVTXN and posts it against the matching item: receipt (add to on-hand),
issue (subtract), or removal (delete the item), printing a one-line-per-transaction
report to QPRINT and flagging items that need re-ordering or review.INVCLSR) — runs the classic single-primary RPG
cycle over a flat, class-sorted extract, accumulating a per-class on-hand total at each
level break (L1) and printing a class line plus an ALERT line when a class total exceeds the
reorder threshold.The three programs are independent CALLs; there is no menu or transaction monitor tying them together
— the operator (or a test driver) calls each by name in sequence. Data flows one direction:
INVSEED populates INVMAS; INVPOST mutates INVMAS from
INVTXN; INVCLSR reports off a pre-summed extract (INVCLSX).
SIMPINV/i is a language-reference application: it was built to stress the classic RPG III / OPM
RPG/400 surface of SteelFrame X's rpg3.js compiler front end (commit fe8342b11), using
real inventory business rules rather than synthetic probes. Every feature it demonstrates —
named constants, alternating compile-time arrays, multiple-occurrence data structures (MODS/OCCURS),
PERRCD>1 layouts, alternating array comma references, O-spec exception output (EXCPT), the
MHxZO zone-move family, and the single-primary RPG cycle with level breaks — is wired into a rule
that a real inventory clerk would recognise (a re-order flag, a class total against a threshold, a packed
status byte). The estate is deliberately compact: two data files, one flat extract, three programs.
It is not, and does not claim to be, a full warehouse-management system; treat it as a faithful, runnable
demonstration of classic RPG III against real data.
The whole estate compiles through the real toolchain: each program is created with
CRTRPGPGM PGM(SIMPINV/xxx) SRCFILE(SIMPINV/QRPGSRC), which routes RPG III source through
rpg3.js's CVTRPGSRC-style conversion to RPG IV and on to the standard build. Source members
live in SIMPINV/QRPGSRC (RPG) and SIMPINV/QDDSSRC (DDS), loaded by the app's own
seedSimpinv() loader — not auto-run by the emulator.
SEED POST REPORT (RPG cycle)
---- ---- ------------------
INVSEED --WRITE--> INVPOST --CHAIN/UPDAT--> INVCLSR (P primary)
INVMAS (5 items) INVMAS (mutated) INVCLSX (flat extract)
DSPLY trace INVTXN (READ, in-key) L1 break on GRP (class)
named consts QPRINT (EXCPT report) QSYSPRT (CLSLN / CLSALR)
ALT array + LOKUP AND/OR conditioning total-time output
MODS ring buffer MHLZO status byte
INVMAS (item master, keyed ITNO) <--- INVSEED writes, INVPOST updates/deletes
INVTXN (txn log, keyed TXNO) ---> INVPOST reads in key order
INVCLSX (program-described flat) ---> INVCLSR cycle-reads (one pre-summed row per class)
A single flow runs: CALL INVSEED loads the five items → transactions are placed in
INVTXN → CALL INVPOST posts each against INVMAS and prints the
posting report → CALL INVCLSR reads the class-sorted extract through the RPG cycle and
prints the class-summary report. The report program reads a separate pre-summed extract
(INVCLSX), not INVMAS directly — see section C.2 for why.
| Object | Type | Role |
|---|---|---|
| INVMAS | PF (DDS) | Item master, keyed on ITNO. |
| INVTXN | PF (DDS) | Transaction log, keyed on TXNO. |
| INVCLSX | Source PF | Program-described flat extract feeding the cycle report (one pre-summed row per class). |
| INVSEED | RPG III pgm | Seed/reference-data loader for INVMAS. |
| INVPOST | RPG III pgm | Transaction poster + posting report. |
| INVCLSR | RPG III pgm | Single-primary-cycle class-summary report. |
| QPRINT | Printer file | INVPOST posting report (USROPN, 132-col). |
| QSYSPRT | Printer file | INVCLSR class-summary report (80-col). |
| QRPGSRC | Source PF | Holds the three RPG members. |
| QDDSSRC | Source PF | Holds the two DDS members. |
The full catalogue is 2 DDS physical files + 1 program-described extract, driven by 3 RPG III programs
writing 2 printer files, with source in 2 source PFs — all in library SIMPINV. Sections
D and F expand each.
SIMPINV/i has no CICS transids and no menu switch. On IBM i each program is reached by name from
a 5250 command-entry line (or a JOBQ/scheduler). 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 SIMPINV — the tested job runs with
LIBL = QSYS QGPL SIMPINV QTEMP and CURLIB = SIMPINV.
| To do this | Type on the command line |
|---|---|
| Seed the item master (5 items) | CALL SIMPINV/INVSEED |
| Post the transaction log against the item master | CALL SIMPINV/INVPOST (or SBMJOB it) |
| Print the class-summary cycle report | CALL SIMPINV/INVCLSR |
| Review the posting report | WRKSPLF → open the QPRINT spool |
| Review the class-summary report | WRKSPLF → open the QSYSPRT spool |
All three programs take no CALL parameters — each reads its data from files, so a scheduled
submission is a bare CALL. None of them is interactive; each runs to completion, DSPLYing a
trace and/or writing a spooled report.
Honest statement: SIMPINV/i has no 5250 display file and no interactive subfile. Unlike a
screen-driven application, none of the three programs presents a panel, prompts for input, or accepts
function keys. There is no DDS DSPF member in the estate — only the two PF
members (INVMAS, INVTXN). The programs are batch/DSPLY workers. What the operator
actually observes is:
DSPLY op-code emits
to the job's message queue (interactively, a one-line message the operator presses Enter through;
in batch, captured in the job log / by the driver). INVSEED DSPLYs its four-line load
trace; INVPOST DSPLYs a line per transaction (the type description, plus status banners
like NO ITEM, BAD KEY, REMOVED).INVPOST writes the posting
report to QPRINT (a 132-column USROPN printer file); INVCLSR writes the
class-summary report to QSYSPRT (80-column). Both are reviewed with WRKSPLF.For orientation only, the DSPLY trace INVSEED produces on a clean seed run looks like this
(four lines, in order — the seed banner, the item count, the class description read back through the
alternating array, and the ring-buffer's final occurrence-1 value):
These are message-queue lines, not a formatted panel — the manual presents them as the trace they are, not a screen. The operationally meaningful output is the two spooled reports (sections C and E).
SIMPINV/i's work is three parameterless batch programs run in sequence: seed, post, and
class report. The first two are ordinary linear RPG III programs (no cycle — an explicit
DOWEQ/READ loop in INVPOST, straight-line writes in
INVSEED). The third, INVCLSR, is the one program that uses the classic
single-primary RPG cycle with an I-spec control level to drive automatic level-break output.
| Program | Purpose | Reads | Writes | Cycle? |
|---|---|---|---|---|
| INVSEED | Load 5 reference items into the item master, with a DSPLY trace. | Compile-time arrays (CLSCD/CLSDS), named constants. | INVMAS (5 WRITE), 4 DSPLY lines. |
No (straight-line). |
| INVPOST | Post each transaction (receipt/issue/removal) against the item master; print a posting report. | INVTXN (READ, key order), INVMAS (CHAIN). |
INVMAS (UPDAT/DELET), QPRINT (EXCPT report), DSPLY per txn. |
No (explicit DOWEQ loop). |
| INVCLSR | Accumulate a per-class on-hand total and print a class-summary + reorder-alert report. | INVCLSX (primary, cycle-read, one pre-summed row per class). |
QSYSPRT (CLSLN always; CLSALR when over threshold). |
Yes (single-primary, L1 break). |
Straight-line: for each of five items it MOVELs an item number and description, sets class / on-hand /
price, runs XLATE to partially upper-case the description, and WRITEs an
INVMASR record. It records the last three item numbers written into a 3-occurrence MODS ring
buffer (RECLOG, positioned with OCUR), and looks class descriptions up in the
alternating compile-time array with LOKUP + comma index. It ends with a four-line DSPLY trace
and SETON LR.
Reads INVTXN in a structured *IN99 DOWEQ *OFF loop. Per transaction it validates
the item number (SCAN for an embedded blank, CHECK for a non-digit suffix,
CHEKR for the last significant character), maps the transaction type to a description through
the comma-indexed TXDSC array, and CHAINs the item master. Then:
ITQOH = ITQOH + TXQTY, UPDAT.ITQOH = ITQOH - TXQTY, UPDAT.DELET the item master record outright.NO ITEM (unknown item number, e.g. IT9999).BAD KEY; the CHAIN/UPDAT are never reached (e.g. IT 007).For each posted item it builds a packed 2-byte status code with MHLZO (a severity nibble
folded onto the item-class letter), evaluates the REORDER indicator (low stock AND high value)
and the REVIEW indicator (class RAW OR class FIN), computes the extended amount, and fires an
EXCPT DETL that prints the detail line — plus a second, AND-conditioned marker line when
both REORDER and REVIEW are on.
INVCLSR is the estate's showcase of the classic RPG cycle. Its primary file
INVCLSX is program-described (an F-spec with the P primary designation and
an I-spec that declares the record fields and the control level). The I-spec names GRP (the
class code) as an L1 control field and QOH as the class on-hand total. The RPG cycle
reads each record automatically; when GRP changes value, an L1 level break fires,
running the total-time C-specs and total-time O-spec output before the next detail cycle.
CLSTOT. The reset is
done in detail time gated on *IN55 (the L1 indicator), not in total time (see
the caveat in F.4).55; a structured
IFxx/ANDxx/END block then sets indicator 34 (ALERT) only when the class
total that just broke exceeds the named-constant threshold CLSTHR (100).CLSLN (conditioned on 55) always prints
CLASS: xxx QTY: nnn; CLSALR (conditioned on 55 AND 34) prints an
ALERT only on breaks over the threshold.Why a pre-summed extract, not INVMAS. The cycle report reads INVCLSX — one
pre-summed row per class (RAW = 4+500 = 504, FIN = 2+60 = 62) — rather than one row per item.
This is deliberate: as documented in the source (RPG3-QA-05, logged and independently reproduced), the
single-primary cycle's control-field comparison spuriously breaks between two consecutive records
that share the same class code. Pre-summing to one row per class sidesteps that trigger while still
exercising a real, non-trivial L1 break (RAW → FIN) plus the guaranteed end-of-file break. On this
data the two class breaks land on opposite sides of the ALERT branch (504 > 100, 62 ≤ 100),
so both branches of the reorder condition are genuinely covered.
Expected QSYSPRT (class-summary report):
CLASS: RAW QTY: 504
ALERT
CLASS: FIN QTY: 62
INVPOST CHAINs INVMAS by item number; the master
must be seeded first or every CHAIN misses (NO ITEM).INVCLSR against the freshly-seeded (pre-posting) on-hand totals — RAW 504,
FIN 62 — via the INVCLSX extract, which is written from those seed figures. The
extract is a separate artefact; whoever builds it decides which point-in-time totals it reflects.DELET item rows and receipts/
issues change on-hand; re-running the full flow starts from a fresh CRTLIB in the test
driver (see E.3).TXNO (key) order.All files are in library SIMPINV, grounded in rpg3-app/src/sources.mjs. The two
business files (INVMAS, INVTXN) are externally-described DDS physical files; the
cycle report reads a third, program-described flat extract (INVCLSX). DDS field types below use
the classic codes: A = character, S = zoned decimal (with a decimal-position digit).
| Field | Type (DDS) | Meaning |
|---|---|---|
| ITNO | 6A | Item number (key), e.g. IT0001. Convention: IT + 4 digits. |
| ITDESC | 20A | Item description (written after XLATE partial upper-casing). |
| ITQOH | 7S,0 | Quantity on hand (integer). |
| ITPRC | 9S,2 | Unit price. |
| ITCLS | 1A | Item class: R raw, F finished goods, S supplies (leftmost byte of the 3-char class code, via MOVEL truncation). |
Seeded item set: IT0001 R/4/199.50, IT0002 R/500/12.75, IT0003 F/2/899.00, IT0004 F/60/145.25, IT0005 S/75/9.99.
| Field | Type (DDS) | Meaning |
|---|---|---|
| TXNO | 6S,0 | Transaction number (key) — also the posting order, since INVPOST reads in key order. |
| ITNO | 6A | Item number the transaction applies to (matched into INVMAS by CHAIN). |
| TXTYP | 1A | Type: 1 receipt (add on-hand), 2 issue (subtract), 3 removal (delete item). |
| TXQTY | 5S,0 | Quantity moved (receipt/issue). |
| TXAMT | 9S,2 | Amount field (present in the record; the report's extended amount is computed as TXQTY×ITPRC). |
Tested transaction set (7 rows, TXNO 1–7): (1) IT0001 receipt 2, (2) IT0002 issue 50,
(3) IT0003 receipt 1, (4) IT0004 issue 10, (5) IT0005 removal, (6) IT9999 receipt 5 (unknown item
→ NO ITEM), (7) IT 007 receipt 1 (malformed key → BAD KEY).
A source physical file of raw fixed-column text, one record per class, pre-sorted by class so the L1
breaks land correctly. It is program-described because I-spec control levels (the L1 that drives the
cycle) are a program-described-file feature — an externally-described file has no I-specs to carry an
L1 entry. The RPG source I-spec nominally declares GRP at cols 13–15 and QOH
at cols 19–23 (the proven convention); the data is deliberately laid out at the actual read
offsets the engine uses (see RPG3-QA-04 in F.4).
| Field | Declared cols | Meaning |
|---|---|---|
| GRP | 13–15 (L1) | Class code (RAW / FIN / ...). The L1 control field. |
| QOH | 19–23 | Pre-summed on-hand total for that class. |
Tested extract rows: RAW / 504 (4+500), FIN / 62 (2+60).
The reference flow — exactly what test/inv_build.mjs drives — is: create the
library and source, compile the three programs, seed, build the extract, report, load the transaction log,
and post. In operator terms:
SIMPINV (CURLIB(SIMPINV)).CRTPF FILE(SIMPINV/INVMAS) SRCFILE(SIMPINV/QDDSSRC) SRCMBR(INVMAS) and the same for
INVTXN.CRTRPGPGM PGM(SIMPINV/INVSEED) SRCFILE(SIMPINV/QRPGSRC) SRCMBR(INVSEED) (and INVPOST,
INVCLSR). A clean compile leaves a *PGM object; a failure spools a listing to
QPRINT.CALL SIMPINV/INVSEED — loads 5 items; check the four-line DSPLY trace.INVCLSX extract (one pre-summed row per class), then
CALL SIMPINV/INVCLSR — review the class-summary spool on QSYSPRT.INVTXN, then CALL SIMPINV/INVPOST
— review the posting report on QPRINT and the DSPLY per-transaction trace.Pre-checks: confirm all three *PGM objects exist; confirm INVMAS is
seeded before INVPOST; confirm the job's library list resolves SIMPINV.
These are the exact figures the build/battle checks against a hand-derived oracle — use them to confirm a healthy run.
INVMAS. IT0001 = class R, qty 4, price 199.50;
IT0003 = class F, qty 2, price 899.00; IT0005 = class S, qty 75,
price 9.99.SEEDED, then the numeric constant 5 (rendered
+000000000000005), then RawMaterls (the alternating-array description read
back through LOKUP + comma index), then IT0004 (the MODS ring-buffer's
occurrence 1, overwritten when item 4 wrapped it).RAWSteel, IT0003 reads
FINgeAR (only the 9 mapped letters r/a/w/f/i/n/s/u/p uppercase; the rest pass through).INVMAS.RECEIPT, ISSUE, RECEIPT, ISSUE, REMOVE, RECEIPT
(6 lines — txn 7's bad key never reaches the comma-index block).NO ITEM (txn 6, unknown IT9999), BAD KEY
(txn 7, malformed IT 007), REMOVED (txn 5, IT0005).QPRINT): banner INVENTORY REPORT; exactly one
REORDER+REVIEW marker line (IT0003: qty 3 < 10 AND price 899 > 500, class F in the
REVIEW set); edit-worded amounts render 1,452.50 (IT0004: 10×145.25) and
399.00 (IT0001: 2×199.50).| Situation | Behaviour | Action |
|---|---|---|
| Compile fails | No *PGM object; a listing spools to QPRINT. | Read the compile listing, fix the source member, re-run CRTRPGPGM. (The build driver prints the failing listing automatically.) |
| INVPOST run before INVSEED | Every CHAIN misses → NO ITEM for all rows. | Seed INVMAS first, then re-post. |
| Re-run INVSEED on a populated master | WRITEs collide with existing keys. | Not idempotent — seed once into an empty master. The tested flow starts from a fresh CRTLIB(SIMPINV). |
| Re-run INVPOST | Destructive/cumulative: receipts/issues re-apply, a removed item stays gone. | Not idempotent — re-run the whole flow from a fresh library to reproduce the verified figures. |
| Unknown item in a transaction | CHAIN miss → DSPLY NO ITEM; no phantom row created; loop continues. | Correct the transaction's ITNO; the bad row is simply skipped, not fatal. |
| Malformed item key (embedded blank) | SCAN gate trips → DSPLY BAD KEY; CHAIN/UPDAT never reached. | Fix the key format (IT+4 digits); the malformed row is skipped safely. |
| Two same-class rows in INVCLSX | Spurious L1 break between consecutive equal keys (RPG3-QA-05). | Feed the cycle report one pre-summed row per class, as the tested extract does (F.4). |
INVSEED/INVPOST mutate INVMAS in place and
the seed is not idempotent, the canonical way to reproduce the verified figures is a full clean run from a
fresh library — exactly what test/inv_build.mjs does (it rebuilds
IBMI_DATA from scratch each run and checks 30+ assertions against a hand-derived oracle).SIMPINV/i is a language-reference application: each program is a vehicle for a cluster of classic RPG III
/ OPM RPG/400 features. This section maps each program to the exact features it demonstrates, cross-
referenced to the rpg3.js deepening (commit fe8342b11) and grounded in the three
RPG members plus sources.mjs (which holds the verified classic column-layout helpers:
IC3 named constant, IDS3/ISF3 MODS, E3 extension spec,
I3r/I3f program-described I-specs, C3 calc, OR3/
OAO3/OF3/O3c output).
SEEDMS (char 'SEEDED'), NITEMS (numeric 5), and the
two XLATE map constants LO/UPP. A named-constant value sits in a 22-column
classic I-spec window (cols 21–42); a longer quoted literal would truncate the closing quote and be
refused by the unbalanced-quote guard, so literals are kept short.CLSCD (class code, 3 chars) alternating with CLSDS (class description, 10
chars), declared PERRCD(1), riding one **CTDATA section (RAWRawMaterls,
FINFinishedGd, SUPShopSupply). Looked up with LOKUP and read through
comma refs like CLSDS,1 / CLSDS,IX. Each 10-char description is chosen to fill
the whole 10 columns (never trailing-blank-padded) — see RPG3-QA-01 in F.4.RECLOG OCCURS 3 with subfield RLITNO (cols 1–6), used as a real
3-slot ring buffer of the last item numbers written, positioned with OCUR. Item 4 explicitly
wraps the position back to occurrence 1, overwriting IT0001 — the final DSPLY of occurrence 1 reads
IT0004, proving program-controlled wrap (not a platform auto-wrap).XLATE 'LO:UPP' ITDX ITDESC: factor 1 is the from:to pair, factor 2 the source, result the
target. The 9-letter map (rawfinsup → RAWFINSUP) upper-cases only those
letters over the full description; the rest pass through. The second constant is UPP not
UP deliberately (RPG3-QA-02, F.4).'RAW') into the 1-char ITCLS keeps only the
leftmost byte (R) — exactly the 1-char class code INVPOST's conditioning expects.LOWSTK (10), HIVAL (500), RPTHDR
('INVENTORY REPORT'), DIGITS ('0123456789' — carried as a
named constant because a C-spec factor literal's 10-column window can't hold the quoted 10-digit string).ITQOH < LOWSTK AND ITPRC > HIVAL
(via IFLT/ANDGT); REVIEW (indicator 31) = ITCLS = 'R'
OR ITCLS = 'F' (via IFEQ/OREQ) — real business rules.PERRCD(1), 3×8 compile-time array
(RECEIPT/ISSUE/REMOVE) indexed by a 1-based TXIX. TXIX
is built with a structured IFEQ chain rather than MOVELing a character digit into a numeric
field (RPG3-QA-03, F.4).MOVE ITCLS STAT then MHLZO SEV STAT folds the
severity flag's zone nibble onto STAT's second byte — the classic space-saving
status-byte technique, character operands throughout (a zoned-numeric source is refused by design).SCAN ' ' ITNO (embedded blank), CHECK DIGITS ITNO (non-digit suffix),
CHEKR ' ' ITNO (rightmost significant char). A malformed key gates out of the CHAIN/UPDAT
body (BAD KEY) instead of an early GOTO.CHAIN (random read of the item master by key), UPDAT (update), DELET
(delete a record for a removal transaction). Sequential READ of the keyed INVTXN
returns rows in key order.QPRINT is UC (USROPN) with explicit OPEN/CLOSE.
Output is exception (EXCPT HDR, EXCPT DETL); the estate uses EXCPT because this
program does not run the RPG cycle (only 'E' records auto-fire outside the cycle). An edit word renders the
extended amount (399.00, 1,452.50). A second DETL record under the
same EXCPT name is AND-folded on indicators 30 and 31, printing the REORDER+REVIEW
marker only when both are on.INVCLSX is a P-designated primary file; the RPG cycle reads each record
automatically and drives detail-time and total-time processing without an explicit read loop.I3r('INVCLSX','01') plus I3f lines declaring GRP (L1 control
field) and QOH. A change in GRP fires the L1 level break. Control levels are a
program-described-file feature — an externally-described file has no I-specs to carry L1.ctl=L1 run at total time: the break sets indicator 55; a structured
IFxx/ANDxx/END block (also all L1) sets indicator 34 (ALERT) only when CLSTOT >
CLSTHR (100) — a real field comparison, not a bare indicator group.CLSTOT resets in detail time gated on *IN55,
not in total time — because the cycle runs all total-time C-specs before the total-time output
(RPG3-QA-06, F.4).T): CLSLN (on 55) always prints
CLASS: xxx QTY: nnn; CLSALR (on 55 AND 34) prints ALERT only
on breaks over threshold — per-record conditioning, not per-field.The source documents a set of confirmed, logged accommodations — some genuine engine quirks in
rpg3.js, some intentional app-side idioms. They are reproduced here so a maintainer reads the
workarounds as deliberate, not accidental. Each is grounded in a source comment.
| ID | Kind | What / why |
|---|---|---|
| RPG3-QA-01 | Engine | cvt3to4 right-trims every source line unconditionally, including **CTDATA data records — a blank-padded fixed-width data record silently loses its trailing bytes and desynchronizes later PERRCD/ALT chunk boundaries. Workaround: every 10-char class description fills all 10 columns (no trailing blanks). |
| RPG3-QA-02 | Engine | A field or named constant literally named UP fails compile with a COBOL syntax error, because rpg.js's reserved-word mangling set (that suffixes -R onto RPG names colliding with COBOL keywords) is missing UP. Workaround: the XLATE target constant is named UPP. |
| RPG3-QA-03 | Engine | A MOVE/MOVEL from a character digit field into a numeric result compiles to a bare COBOL COMPUTE of the alphanumeric field, which GnuCOBOL rejects ("not numeric") — confirmed with a 5-line repro, not RPG-III-specific. Workaround: TXIX is built with a structured IFEQ chain. |
| RPG3-QA-04 | Engine | A program-described DISK file read through the materialized-source-member path ignores the declared I-spec from/to columns and always extracts from (declared_col − 12) — a 12-byte source-sequence-prefix width. Workaround: INVCLSX data is laid out at the actual read offsets (GRP cols 1–3, QOH cols 7–11) while the I-spec still declares the proven 13–15 / 19–23. |
| RPG3-QA-05 | Engine | The single-primary cycle's control-field comparison spuriously breaks between two consecutive records that share the same class code (independently reproduced). Workaround: feed the cycle one pre-summed row per class, so no two adjacent rows share a key. |
| RPG3-QA-06 | App idiom | Not a platform bug: the cycle driver runs all total-time C-specs before the total-time O-spec output, so a total-time reset zeroes the total before it prints. Correct classic idiom used here: reset in detail time gated on *IN55. |
LO:UPP map, and banners are all kept short by design, and wider strings (the
digit set) ride named constants.CRTRPGPGM.INVCLSR.GRP) changes value, the
cycle fires total-time processing for the group that just ended (here, per class).ctl=L1.C) with a fixed value, e.g. LOWSTK=10 — the
RPG III way to name a magic number or a literal.**
separator. TXDSC, and the alternating CLSCD/CLSDS, are compile-time
arrays.CLSDS,1.PERRCD(1) (one logical entry per record).OCCURS); OCUR
positions to a given occurrence. RECLOG OCCURS 3 is used as a ring buffer.name,index syntax, e.g. TXDSC,TXIX or
CLSDS,1.INVPOST uses all four.INVPOST uses MHLZO.EXCPT op-code fires named O-spec output records on demand. Outside
the RPG cycle, only exception ('E') records auto-fire, which is why INVPOST uses EXCPT.UC): the program opens and closes the file itself with
OPEN/CLOSE rather than at program start/end. QPRINT is USROPN.INVMAS/INVTXN are externally described; INVCLSX
is program described (so it can carry the L1 control level).CRTRPGPGM routes RPG III source through SteelFrame X's rpg3.js
front end (a CVTRPGSRC-style RPG III → RPG IV conversion) and on to the standard build.