ORDERIS/i is an order-to-cash application: customer credit control, order capture with
volume-break pricing, stock allocation and back-order handling, invoicing, cash application, AR ageing,
a revenue GL post, and an annual credit-limit review with year-end roll. The processing logic lives in
fixed-form and free-form ILE RPG programs (one SQLRPGLE for the GL post, one ILE COBOL for the AR
control report), orchestrated by CL cycle programs; DB2 for i holds the data as keyed physical and
logical files plus one SQL table. This manual is the reference for the operator who runs the four
periodic cycles and the online inquiry screens, and for the developer maintaining the application. It
is grounded entirely in the committed source (order-app/src/sources.mjs,
order-app/src/seed.mjs, and the order-app/test/oe_*.mjs drivers).
ORDERIS/i runs the full order-to-cash cycle for a distributor:
The application is a classic IBM i batch estate. There is no single rules engine and no stored-procedure layer; each business rule is coded in the RPG program that owns that step:
READ/READE
/CHAIN, computes in packed-decimal work fields, and updates the master files in place.
OEGLPST is the one SQLRPGLE (embedded SQL against the GL table);
OEARRPT is ILE COBOL.OEDAILY, OEWEEK,
OEMONTH, OEYEAR — simply CALL the posting programs in
the correct order. OESETUP creates every object and compiles every program.OEGLDIST).
Uniqueness and access paths are enforced by the DDS keys.The benefit for operations: every cycle is a bare CALL of one CL program, each program
reports a one-line DSPLY tally, and re-runnability is engineered per program through
derived keys (see F.3) rather than a control table. Everything runs in library ORDERIS.
ONLINE (5250) DAILY OEDAILY WEEKLY OEWEEK
------------ -------------- -------------
OEMENU (menu) OEPRICE (volume pricing) OEBOREL (b/o release sweep)
opt 1 -> OECUSTIQ OECREDIT (credit check) OEBOOK (order-book report)
opt 2 -> OEORDIQ OEALLOC (allocate/back-ord)
(subfile) OEINVGEN (invoice + AR up) MONTHLY OEMONTH
OECASHAP (cash + AR down) -------------
OEAGE (AR ageing buckets)
SETUP ANNUAL OEYEAR OESTMT (statement print)
----- ------------- OEGLPST (revenue GL post)
OESETUP OECRREV (credit review) OEARRPT (AR control, COBOL)
OEREFLD (seed data) OEARCHIV (archive+year reset)
OEARRPT (AR control)
DATA: OECUST OEITEM OEORDH OEORDL OEPRIC OEINVH OEAROP OECASH OESHIS
OEORDLLF(by ITEM) OEARDLF(by DUEDATE) OEGLDIST(SQL)
A single day flows: capture orders into OEORDH/OEORDL (status
'N') → OEDAILY prices them, credit-checks them (approve 'A'
or hold 'H'), allocates against OEITEM stock (back-ordering shortfalls),
invoices allocated orders (writing OEINVH + OEAROP + OESHIS and
raising OECUST.BALANCE), and applies receipts from OECASH (lowering the
balance). Every money movement leaves a durable OESHIS audit row.
| Object | Type | Role |
|---|---|---|
| OECUST | PF | Customer master (credit limit, live AR balance, terms, hold). |
| OEITEM | PF | Product master (list price, on-hand, allocated, tax code). |
| OEORDH | PF | Order header (status, stamped net/tax/total, invoice no). |
| OEORDL | PF | Order lines (composite key ORDNO+LSEQ). |
| OEORDLLF | LF | Order lines keyed by ITEMNO (demand-by-product path). |
| OEPRIC | PF | Volume-discount price breaks (ITEMNO+BRKQTY). |
| OEINVH | PF | Invoice header (one per invoiced order). |
| OEAROP | PF | AR open items (the receivables ledger; CUSTNO+INVNO). |
| OEARDLF | LF | AR open items keyed by ARDUEDT (ageing path). |
| OECASH | PF | Cash receipts. |
| OESHIS | PF | Sales-history / durable audit trail. |
| OEGLDIST | SQL table | Revenue/AR/tax GL distribution (+ index OEGLDACC). |
| OECUSTD / OEORDD / OEMENUD | DSPF | Customer inquiry / order-subfile inquiry / menu. |
| OEAGEP | PRTF | AR ageing / order-book printer report. |
| OEPRICE OECREDIT OEALLOC OEINVGEN OECASHAP | RPGLE | The daily order-to-cash chain. |
| OEBOREL OEBOOK | RPGLE | Weekly back-order release + order-book report. |
| OEAGE OESTMT OECRREV OEARCHIV | RPGLE | Monthly ageing/statement; annual review/archive. |
| OEGLPST | SQLRPGLE | Monthly revenue GL post (embedded SQL). |
| OEARRPT | CBLLE | ILE COBOL AR control report. |
| OEREFLD | RPGLE | Reference-data seed (customers/products/breaks). |
| OECUSTIQ OEORDIQ OEMENU | RPGLE | Interactive inquiry programs + menu. |
| OESETUP | CLP | Object create + program compile. |
| OEDAILY OEWEEK OEMONTH OEYEAR | CLP | The four job-cycle drivers. |
The full catalogue is 9 PFs + 2 LFs + 3 DSPFs + 1 PRTF + 1 SQL table, driven by 16 RPG programs, 1 COBOL program and 5 CL programs. Sections D and F expand each.
ORDERIS/i has no CICS transaction identifiers and no transid switch. On IBM i, each program is
reached by name from a 5250 command-entry line (or a JOBQ/scheduler for the cycle programs). 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 ORDERIS — the tested
jobs run with LIBL = QSYS QGPL ORDERIS QTEMP and CURLIB = ORDERIS.
| To do this | Type on the command line |
|---|---|
| Open the main menu (routes to the two inquiries) | CALL ORDERIS/OEMENU |
| Open customer inquiry directly | CALL ORDERIS/OECUSTIQ |
| Open order inquiry (subfile) directly | CALL ORDERIS/OEORDIQ |
| Run the daily order-to-cash cycle | CALL ORDERIS/OEDAILY (or SBMJOB it) |
| Run the weekly back-order/order-book cycle | CALL ORDERIS/OEWEEK |
| Run the monthly AR close | CALL ORDERIS/OEMONTH |
| Run the annual credit review / year-end roll | CALL ORDERIS/OEYEAR |
| Create objects & compile (first-time / rebuild) | CALL ORDERIS/OESETUP then CALL ORDERIS/OEREFLD |
The four cycle programs take no CALL parameters and drive their run dates from constants inside
the posting programs (the invoicing run stamps 20260801; ageing/GL run at
20260930), so a scheduled submission is a bare CALL. Only the three inquiry
programs are interactive; the cycles run to completion and DSPLY a one-line tally per step.
Honest statement: the online layer is inquiry-only. There is no online order-entry, payment-posting or maintenance screen — orders and receipts are created into the files (batch load or capture upstream) and the RPG cycles do all the processing. The three DDS programs let an operator look at a customer or an order; they never update a master file.
A plain screen (record OEMENU) offering option 1 Customer Inquiry (calls
OECUSTIQ) and 2 Order Inquiry (calls OEORDIQ). Any other non-blank
option shows Invalid option. F3 ends the program.
Keys a customer number into ICUST, CHAINs OECUST, and shows the
master fields plus the derived available credit (limit less balance) — the figure the credit
check actually reasons about. A miss shows Customer not found.
The app's subfile screen. Keys an order number into IORDNO, CHAINs
OEORDH for the header (customer, status, hold reason, order total), then loads the order's
lines into subfile OSFL under control record OCTL (SFLPAG(5) per
page, SFLSIZ(20), ROLLUP/ROLLDOWN paging). A credit-held order
renders its status H and hold reason (e.g. CL) on screen.
| Field | Type (DDS) | Shows |
|---|---|---|
| SSEQ | 2Y,0 output | Line sequence (LSEQ). |
| SITEM | 8A output | Product number (ITEMNO). |
| SQTY | 10A output | Ordered quantity (LQTY). |
| SALL | 10A output | Allocated / shippable now (LALLOC). |
| SBO | 10A output | Back-ordered remainder (LBORD). |
| SNET | 12A output | Extended net (LNET). |
| SSTAT | 1A output | Line status (LSTAT: O/A/B/I/X). |
SFLCLR via
*IN31), and SFLDSP/SFLDSPCTL are driven off an indicator
(N31) so an order number that does not exist shows Order not found. and an
empty subfile rather than redisplaying the previous enquiry's rows — a real-DDS fix noted
in the program source.Honest statement: ORDERIS/i does not model a true four-eyes maker–checker / separate-authorization workflow. No "one user posts, a second user approves" step exists in the code; the batch cycles process whatever orders and receipts are present. The manual documents the control model the application does have:
OECREDIT is the real control point: an order that breaches
available credit, or belongs to a manually-held (CHOLD='Y') or inactive customer, is
set to status 'H' with a reason (CL credit limit, MH manual
hold, CS customer not active). OEALLOC then skips any order not in
status 'A', so a held order reserves no stock and can never be invoiced — the assertion the
tests verify.OECUST.BALANCE is moved only two ways:
OEINVGEN raises it by exactly the invoice total, OECASHAP lowers it by
exactly the amount applied. Nothing else touches it. This is the invariant the daily suite asserts
in both directions.HTYPE='I'), cash ('C'), back-order
release ('B'), year-end archive ('Y'). The sales history is the
after-the-fact accountability trail, reconstructable per customer.CHAIN(EN) (see F.3), so a repeated
cycle is a safe no-op rather than a second charge.In sum, the control posture is a credit-control gate + a strict AR-balance invariant + a durable audit trail + derived-key idempotency, all enforced in the posting programs, rather than a segregation-of-duties approval workflow.
ORDERIS/i runs as four periodic cycles rather than one monolithic nightly job: a daily
order-to-cash chain, a weekly back-order/report cycle, a monthly AR close, and an
annual credit review / year-end roll. Each is one CL program that CALLs its posting
programs in order and ends with an SNDPGMMSG completion banner. None takes CALL parameters;
run dates are constants inside the posting programs.
-- run a cycle interactively CALL ORDERIS/OEDAILY -- or submit it to a job queue / scheduler (bare CALL, no parms) SBMJOB CMD(CALL PGM(ORDERIS/OEMONTH)) JOB(OEMONTH)
| Program | Cycle | Purpose | Reads / writes | DSPLY tally |
|---|---|---|---|---|
| OEPRICE | Daily | Price open lines from volume breaks; stamp header net/tax/total (tax 8% on 'S'). | OEORDH/OEORDL/OEITEM/OEPRIC. | OEPRICE PRICED=n SKIP=n |
| OECREDIT | Daily | Credit-check 'N' orders vs available credit; hold with reason CL/MH/CS. | OEORDH/OECUST. | OECREDIT APPROVED=n HELD=n SKIP=n |
| OEALLOC | Daily | Allocate approved lines against free stock; back-order the shortfall. Skips held orders. | OEORDH/OEORDL/OEITEM. | OEALLOC ALLOC=n BORD=n SKIP=n |
| OEINVGEN | Daily | Invoice allocated orders; write OEINVH+OEAROP+OESHIS; raise OECUST.BALANCE; relieve stock. | OEORDH/OEORDL/OEITEM/OEINVH/OEAROP/OECUST/OESHIS. | OEINVGEN INVOICED=n SKIP=n |
| OECASHAP | Daily | Apply unapplied receipts to open items; lower OECUST.BALANCE by what is applied. | OECASH/OEAROP/OECUST/OEINVH/OESHIS. | OECASHAP APPLIED=n SKIP=n |
| OEBOREL | Weekly | Release back-ordered lines as stock arrives; lift the header back to 'A' to bill the remainder. | OEORDH/OEORDL/OEITEM/OESHIS. | OEBOREL RELEASED=n STILLBO=n |
| OEBOOK | Weekly | Print the un-invoiced order book (held/approved/back-order) on the PRTF. | OEORDH/OEORDL/OECUST → OEAGEP. | OEBOOK ORDERS=n VALUE=n |
| OEAGE | Monthly | Age open items into buckets 0/1/2/3 via the 30/360 serial; read oldest-due-first through OEARDLF. | OEAROP (via OEARDLF). | OEAGE B0=n B1=n B2=n B3=n |
| OESTMT | Monthly | Print the AR statement/ageing report over the due-date path. | OEAROP (via OEARDLF)/OECUST → OEAGEP. | OESTMT LINES=n OPEN=n |
| OEGLPST | Monthly | Post balanced DR AR / CR Revenue / CR Tax from the invoice register into OEGLDIST. | OEINVH → OEGLDIST (embedded SQL). | OEGLPST BATCH=n ROWS=n |
| OEARRPT | Monthly/Annual | ILE COBOL AR control report: raised / open / item count / overdue over the ledger. | OEAROP. | OEARRPT RAISED/OPEN/ITEMS/OVERDUE |
| OECRREV | Annual | Credit-limit review: halve+hold bad payers (bucket 3), reward good ones (+20%); stamp CREVYR. | OECUST/OEAROP. | OECRREV UP=n DOWN=n SAME=n |
| OEARCHIV | Annual | Write one turnover-archive row per customer; close fully-settled AR items (year-end reset). | OECUST/OESHIS/OEAROP. | OEARCHIV ARCHIVED=n CLOSED=n SKIP=n |
| OEREFLD | Setup | Seed 4 customers, 5 products, 4 volume breaks. | OECUST/OEITEM/OEPRIC. | OEREFLD CUST=4 ITEM=5 PRICE=4 |
Pricing. For each order line, OEPRICE walks the item's OEPRIC breaks in
ascending quantity and keeps the last break the line quantity qualifies for — the biggest
discount earned. Unit = list − discount%; line net = (allocated if >0 else ordered) × unit.
The header carries the summed net, 8% tax on 'S'-rated lines only, and the total. It is
idempotent: it recomputes from LQTY every run, so a re-run lands on identical numbers.
Credit check. OECREDIT considers only 'N' orders. Available =
CRLIMIT − BALANCE; if the order total exceeds it → hold CL; a
CHOLD='Y' customer → MH; a non-active or unknown customer →
CS; otherwise approve ('A').
Allocation. OEALLOC processes only 'A' orders. Free = on-hand less
already-allocated; it takes min(free, ordered), back-orders the rest, values the line on what will ship,
and sets the order to 'B' only if nothing at all could be allocated.
Invoicing. OEINVGEN invoices 'A' orders: invoice number
IN0+order digits (stable), relieves on-hand and allocation for what shipped, raises
BALANCE by the total, writes the invoice, the AR open item (due = invoice date + terms),
and an OESHIS row. A line with a back-ordered remainder stays open for the weekly sweep.
Cash. OECASHAP applies each 'U' (unapplied) receipt to its named open
item, taking only what is owed; an over-payment is part-applied (RCSTAT='P'). Balance falls
by exactly what was applied.
Expected DSPLY (three seeded orders, one cash receipt): OEPRICE PRICED=3 SKIP=0 OECREDIT APPROVED=2 HELD=1 SKIP=0 ORD00002 held CL (720.00 > 500.00) OEALLOC ALLOC=n BORD=1 SKIP=1 ORD00003 line back-orders 7 of 12; held order skipped OEINVGEN INVOICED=2 SKIP=1 IN000001 = 1922.40, IN000003 = 67.50 OECASHAP APPLIED=1 SKIP=0 1000.00 vs IN000001 -> balance 922.40
OEBOREL walks back-ordered ('B') lines and re-allocates whatever stock has
arrived, flipping a line back to 'A' when it can be filled in full, re-valuing on the new
allocation, and writing a 'B'-type OESHIS row. Crucially it lifts a
partially-invoiced header ('I') back to 'A' so
OEINVGEN bills the released remainder — a volume-simulation fix noted in the source.
OEBOOK then prints the still-open book.
Expected DSPLY (7 units restocked, back-order fully released): OEBOREL RELEASED=1 STILLBO=0 ORD00003 line: 7 allocated, header B/I -> A OEBOOK ORDERS=n VALUE=n
OEAGE ages every open item using the 30/360 serial
((yyyy*12)+mm)*30+dd: days = serial(run) − serial(due), bucket 0 current, 1 (1–30),
2 (31–60), 3 (61+). OESTMT prints the ageing over the due-date path. OEGLPST
sums the invoice register and posts balanced DR 1200-AR / CR 4000-REV / CR 2200-TAX into
OEGLDIST, continuing the GLSEQ sequence from what is already posted and only
posting movement since the last close (so month two does not collide on the primary key).
OEARRPT (COBOL) reports the ledger totals.
Expected DSPLY (one item in each of four buckets seeded): OEAGE B0=1 B1=2 B2=1 B3=1 OESTMT LINES=n OPEN=n OEGLPST BATCH=202609 ROWS=n DR 1200-AR 1989.90; CR 4000-REV 1842.50; CR 2200-TAX 147.40 OEARRPT RAISED / OPEN / ITEMS / OVERDUE
OECRREV reviews each active customer once per review year (guarded by
CREVYR): anything in ageing bucket 3 → limit halved and manual hold set; else balance
≤ 25% of limit → limit +20%; else unchanged. OEARCHIV writes one turnover-archive
row per trading customer (sum of that customer's invoice history) and closes every fully-settled AR open
item (ARSTAT='C').
Expected DSPLY: OECRREV UP=n DOWN=n SAME=n C00002 500.00 -> 250.00 + hold; C00004 25000 -> 30000 OEARCHIV ARCHIVED=n CLOSED=n SKIP=n
OEPRICE must run
first because the credit check needs a value; OECREDIT before OEALLOC so
a held order is skipped; OEALLOC before OEINVGEN so the invoice ships the
allocated quantity; OECASHAP last.OEBOREL only releases lines the daily allocation left in
'B', and lifts partially-invoiced headers back to 'A' so the next daily
OEINVGEN bills the remainder.OEAGE; the archive sums the year's invoice history.All files are in library ORDERIS, grounded in order-app/src/sources.mjs.
Dates are stored as signed numeric YYYYMMDD; money and quantities are packed decimal
(P); prices are 4-decimal packed (11P4), most values 2-decimal.
| Field | Type | Meaning |
|---|---|---|
| CUSTNO | 6A | Customer number (key). |
| CNAME | 30A | Customer name. |
| CTOWN | 20A | Town. |
| CRLIMIT | 11P,2 | Credit ceiling the credit check tests against. |
| BALANCE | 11P,2 | Live AR balance; moved ONLY by invoicing (up) and cash (down). |
| CTERMS | 3S,0 | Payment terms in days (drives the AR due date). |
| CDISC | 5P,2 | Customer discount percent (carried, informational). |
| CHOLD | 1A | 'Y' parks the customer on manual hold regardless of arithmetic. |
| CSTAT | 1A | Customer status ('A' active). |
| CREVYR | 4S,0 | Review year stamp — the annual credit-review idempotency guard. |
| Field | Type | Meaning |
|---|---|---|
| ITEMNO | 8A | Product number (key). |
| IDESC | 30A | Description. |
| IUOM | 3A | Unit of measure. |
| LISTPR | 11P,4 | Pre-discount unit price. |
| ONHAND | 9P,2 | On-hand quantity. |
| ALLOC | 9P,2 | Allocated (reserved) quantity; free stock = ONHAND − ALLOC. |
| TAXCD | 1A | 'S' standard-rated (8% tax), 'Z' zero-rated. |
| ISTAT | 1A | Product status ('A' active). |
| Field | Type | Meaning |
|---|---|---|
| ORDNO | 8A | Order number (key), e.g. ORD00001. |
| CUSTNO | 6A | Owning customer. |
| ORDDT | 8S,0 | Order date (YYYYMMDD). |
| OSTAT | 1A | N new, H on credit hold, A approved/allocated, B back-ordered, I invoiced, X cancelled. |
| OHOLDR | 2A | Hold reason (CL / MH / CS) when OSTAT='H'. |
| ONETVAL / OTAXVAL / OTOTVAL | 11P,2 | Stamped net / tax / total (pricing & invoicing). |
| OINVNO | 8A | Invoice number once invoiced. |
| Field | Type | Meaning |
|---|---|---|
| ORDNO / LSEQ | 8A / 3S,0 | Order + line sequence (composite key → needs a KLIST to re-CHAIN). |
| ITEMNO | 8A | Product. |
| LQTY | 9P,2 | Ordered quantity. |
| LALLOC | 9P,2 | Allocated (shippable now). |
| LBORD | 9P,2 | Back-ordered remainder = LQTY − LALLOC. |
| LUNITP | 11P,4 | Unit price after the volume break. |
| LDISCP | 5P,2 | Discount percent taken off list. |
| LNET | 11P,2 | Extended net (valued on what ships). |
| LSTAT | 1A | O open, A allocated, B back-order, I invoiced, X cancelled. |
| Field | Type | Meaning |
|---|---|---|
| ITEMNO / BRKQTY | 8A / 9P,2 | Product + quantity threshold (key, ascending walk). |
| BRKDISC | 5P,2 | Discount percent once the line reaches BRKQTY. |
| PSTAT | 1A | Break status ('A' active). |
Seeded breaks: PR100001 10+→10% & 50+→15%; PR100002 5+→5%; PR100004 20+→20%; others none.
| Field | Type | Meaning |
|---|---|---|
| INVNO | 8A | Invoice number (key), DERIVED from the order (IN0+order digits). |
| ORDNO / CUSTNO | 8A / 6A | Source order and customer. |
| INVDT | 8S,0 | Invoice date. |
| INVNET / INVTAX / INVTOT | 11P,2 | Net / tax / total. |
| INVSTAT | 1A | 'O' open, 'P' paid (set by cash application). |
| Field | Type | Meaning |
|---|---|---|
| CUSTNO / INVNO | 6A / 8A | Customer + invoice (key; groups a customer's items). |
| ARINVDT / ARDUEDT | 8S,0 | Invoice date / due date (= invoice date + terms). |
| ARAMT | 11P,2 | Original invoice total. |
| AROPEN | 11P,2 | Outstanding amount (reduced as cash is applied). |
| ARAGE | 1S,0 | Ageing bucket 0/1/2/3 stamped by OEAGE. |
| ARSTAT | 1A | 'O' open, 'P' settled, 'C' closed (year-end reset). |
| Field | Type | Meaning |
|---|---|---|
| RCPTNO | 8A | Receipt number (key). |
| CUSTNO / RCINVNO | 6A / 8A | Customer + nominated invoice. |
| RCDT | 8S,0 | Receipt date. |
| RCAMT | 11P,2 | Amount tendered. |
| RCAPPL | 11P,2 | Amount actually applied (≤ open balance). |
| RCSTAT | 1A | 'U' unapplied, 'A' fully applied, 'P' part-applied. |
| Field | Type | Meaning |
|---|---|---|
| HSEQ | 8S,0 | Sequence (key) — disjoint numeric ranges per program (see F.3). |
| CUSTNO / HITEM | 6A / 8A | Customer + item (or a marker like *ORDER/*CASH). |
| HTYPE | 1A | I invoice, C cash, B back-order release, Y year-end archive. |
| HDT / HQTY / HAMT | 8S,0 / 9P,2 / 11P,2 | Date / quantity / amount (cash amounts are negative). |
| HREF / HMEMO | 8A / 25A | Reference (order/invoice) + free-text memo. |
| Field | Type | Meaning |
|---|---|---|
| GLSEQ | DECIMAL(8,0) | Monotonic sequence (PK); continued across months. |
| GLBATCH | DECIMAL(6,0) | Post batch (YYYYMM). |
| ACCT / DRCR | CHAR(9) / CHAR(1) | Account (1200-AR, 4000-REV, 2200-TAX) + D/C. |
| AMT | DECIMAL(11,2) | Posted amount. |
| GLREF / GLDT | CHAR(8) / DECIMAL(8,0) | Reference + posting date. |
OEORDL keyed ITEMNO, ORDNO
(non-unique): the demand-by-product path.ARDUEDT, CUSTNO: the oldest-due-first
path the monthly ageing (OEAGE/OESTMT) walks. The PF is customer-keyed; the
LF is due-date-keyed — genuinely different orderings.ORDERIS (ADDLIBLE LIB(ORDERIS) —
the cycle CL programs already do this).OEORDH/OEORDL at status
'N', and any receipts into OECASH at RCSTAT='U'.SBMJOB CMD(CALL PGM(ORDERIS/OEDAILY)).OEWEEK; review
OEBOREL RELEASED= and the order-book report.CALL ORDERIS/OEMENU (customer / order inquiry).Post-checks after the daily cycle:
OECREDIT HELD= equals the orders that breached credit or a
manual/inactive hold, and those are status 'H' reserving no stock.OECUST.BALANCE by exactly its total —
e.g. a 1,922.40 invoice moves a 0.00 balance to 1,922.40; a held customer's balance never moves.OEDAILY raises
no further invoices and does not double the balance (derived invoice key).OEMONTH. Confirm the ageing spread, the statement, and the GL post.SELECT ACCT, DRCR, SUM(AMT) FROM ORDERIS.OEGLDIST GROUP BY ACCT, DRCR;
Reconciling figures (the same ones the cycles suite checks against a hand-derived oracle):
SUM(DR) = SUM(CR) exactly and non-zero. From the two invoiced
orders: DR 1200-AR = 1989.90 (1,922.40 + 67.50), CR 4000-REV = 1842.50
(1,780.00 + 62.50), CR 2200-TAX = 147.40 (142.40 + 5.00); revenue + tax = the AR debit.ARAGE equals its 30/360 days-overdue bucket at
the run date (0 current, 1 = 1–30, 2 = 31–60, 3 = 61+). OEAGE re-run is a pure
recompute — identical buckets, same ledger count.OEARRPT RAISED = SUM(ARAMT), OPEN = SUM(AROPEN), ITEMS =
row count, OVERDUE = items in a non-zero bucket.Year-end (annual): submit OEYEAR; confirm the credit review moved the right
limits (a bucket-3 debtor's limit halved and put on hold; a well-settled account raised 20%), one
turnover-archive row exists per trading customer (archived turnover = that customer's invoiced history),
and fully-settled AR items are closed while genuinely-open items stay open.
Each program DSPLYs a one-line tally; a chain that ran clean ends with its CL completion banner
(e.g. ORDERIS daily order-to-cash cycle complete.). Re-runnability is engineered per
program.
| Situation | Behaviour | Action |
|---|---|---|
| Re-run OEPRICE / OEDAILY | Pricing recomputes from LQTY; the chain is idempotent. | Safe. Re-run raises no new invoices and does not move the balance again. |
| Re-run OEINVGEN | Derived invoice key already exists; CHAIN(EN) refuses it. | Never double-invoices. Register count and balance unchanged. |
| Re-run OECASHAP | Receipt-derived history key already present; receipt already flipped off 'U'. | Applies nothing further; balance does not fall twice. |
| Re-run OEALLOC | Judges the header from PERSISTED line state, not this pass. | Re-run is a no-op; allocation is not doubled (stays reserved once). |
| Re-run OEBOREL | Nothing left in 'B'; release-history key guards each line. | Releases nothing further; stock reservation not doubled. |
| Re-run OEGLPST | Continues GLSEQ from max posted; posts only movement since last close; checks SQLCOD. | Second month does not collide on the PK, and a refused insert is NOT counted as posted. |
| Re-run OECRREV | CREVYR stamp skips an already-reviewed customer. | A second review leaves every limit exactly where it was (no compounding). |
| Re-run OEARCHIV | Customer-derived archive key guards the row. | Archives nothing further; no duplicate archive rows. |
| Order stuck on hold | Held order never allocates or invoices. | Clear the cause (raise limit / lift CHOLD / activate customer), reset OSTAT to 'N', re-run OEDAILY. |
OESHIS with a unique key, and
the AR balance moves only through invoicing and cash, any cycle's effect is fully reconstructable after
the fact for reconciliation and recovery.The complete program surface, from order-app/src/sources.mjs. All objects are in library
ORDERIS; the seed loader is order-app/src/seed.mjs.
Re-runnability is achieved without a control table: each posting program computes a stable
HSEQ from a business key in a disjoint numeric range and guards it with
CHAIN(EN) (chain, no-lock, return found/not-found), so a repeat is refused.
| Program | Key derivation | Guard |
|---|---|---|
| OEINVGEN | 10000000 + order digits (and INVNO = IN0+order digits) | CHAIN(EN) OEINVHR / OESHISR |
| OECASHAP | 20000000 + receipt digits | CHAIN(EN) OESHISR + RCSTAT flip |
| OEBOREL | 30000000 + order digits × 100 + line | CHAIN(EN) OESHISR (per line) |
| OEARCHIV | 50000000 + customer digits | CHAIN(EN) OESHISR (per customer) |
| OECRREV | (no history row) — guarded by the CREVYR field | skip if CREVYR = review year |
| OEGLPST | GLSEQ continued from MAX(GLSEQ); posts movement since last close | SQLCOD checked; PK on GLSEQ |
Several programs read OEORDL with a READE loop, then read other files
(OEITEM, OEPRIC) whose I/O moves the update-file cursor. To update the line
they re-CHAIN it by its composite key using a KLIST (WORD+WSEQ) before
UPDATE — and likewise re-CHAIN the header (WORD) before updating it. This
is why the source carries LKEY/ARKEY KLISTs and comments such as
"re-CHAIN the line: the item update moved this file's cursor". It is native-I/O discipline, not a bug.
| Field | Value | Meaning |
|---|---|---|
| OSTAT (order) | N / H / A / B / I / X | New / held / approved-allocated / back-ordered / invoiced / cancelled. |
| OHOLDR (hold reason) | CL / MH / CS | Credit-limit exceeded / manual hold / customer not active. |
| LSTAT (line) | O / A / B / I / X | Open / allocated / back-order / invoiced / cancelled. |
| INVSTAT (invoice) | O / P | Open / paid. |
| ARSTAT (AR item) | O / P / C | Open / settled / closed (year-end). |
| RCSTAT (receipt) | U / A / P | Unapplied / fully applied / part-applied. |
| ARAGE (bucket) | 0 / 1 / 2 / 3 | Current / 1–30 / 31–60 / 61+ days overdue. |
| TAXCD | S / Z | Standard-rated (8%) / zero-rated. |
CRLIMIT − BALANCE. An order total over
this is held with reason CL.SBMJOB CMD(CALL PGM(ORDERIS/OEDAILY)).((yyyy*12)+mm)*30+dd; days overdue = serial(run) − serial(due).