BAZAAR/i — Retail Point of Sale & Merchandise Inventory

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

BAZAAR/i is a multi-store specialty-retail application: a store and SKU master with a store-level price book, POS basket capture with mixed tender and returns/refunds, nightly stock depletion and sales/tax GL distribution, per-till cash-up and variance reporting, weekly store replenishment and slow/fast-mover ranking, monthly markdown management and department margin analysis, and an annual stocktake, price-book roll and sales archive. It is a classic IBM i mixed-language estate: RPG (fixed-form and free-form) for the batch and online programs, one SQLRPGLE program for the GL distribution, ILE COBOL for the till audit report, DDS for the physical/logical/display/printer files, and CL for the setup and the four job cycles. This manual is the reference for the operator who runs the online screens and the periodic cycles, and for the developer maintaining the application. It is grounded entirely in the committed source (retail-app/src/sources.mjs, src/seed.mjs, and the test/rt_*.mjs drivers).

Contents

A. Overview & Architecture ↑ top

A.1 What it does

BAZAAR/i runs the merchandising and till life of a small chain across trading days, weeks, months and the trading year:

A.2 Mixed-language architecture: DDS data, RPG/COBOL logic, CL orchestration

Everything runs in library BAZAAR. The application is a conventional IBM i layering, not a two-layer SQL-PL design:

The benefit for operations: each cycle is a single CL you submit; each program is a small, single-purpose step that DSPLYs a one-line result you can read back; and the data model's composite (STORE, SKU) keys mean every figure is genuinely per-store — the same SKU can carry a different price, a different promotion and a different book at each store.

A.3 Component & flow

  ONLINE                 DAILY                 WEEKLY / MONTHLY / ANNUAL
  ------                 -----                 -------------------------
  RTMENU (5250)          RTDAILY:              RTWEEK:
    opt 1 -> RTSKUIQ       RTPOSCP  (price)      RTREPLG (suggest+rank)
    opt 2 -> RTTXNIQ       RTPOSPT  (deplete)    RTONORD (raise on-order)
             (subfile)     RTTILLR  (cash-up)  RTMONTH:
                           RTTILPR  (print)      RTMKDNA (markdown)
                           RTGLPST  (GL, SQL)    RTDSUMA (dept margin)
                           RTTLRPT  (COBOL)    RTYEAR:
                                                 RTSTKTA (stocktake var)
                                                 RTPBRLL (price-book roll)
                                                 RTARCH  (archive+revalue)

  MASTER / TXN FILES (library BAZAAR)
    RTSTORE RTDEPT RTSKU RTPRICE RTSTOCK   <-- reference (RTREFLD seeds)
    RTSALE (hdr) RTSLIN (lines) RTTEND (tender)  <-- POS basket
    RTTILL RTGLDIST RTREPL RTMKDN RTSTKTK RTDSUM <-- cycle outputs
    LFs: RTSTKLF (SKU,STORE)  RTSLLF (SKU,TXNO)  RTSALF (STORE,DATE,TXNO)

A single trading day flows: the lane writes captured baskets (TXSTAT='C') into RTSALE/RTSLIN/RTTENDRTDAILY prices them (RTPOSCP), posts them and depletes stock (RTPOSPT, flipping CP), cashes up the tills (RTTILLR), prints the reconciliation (RTTILPR) and distributes the money to the GL (RTGLPST), and the COBOL audit (RTTLRPT) re-proves the till invariant.

A.4 Object inventory

ObjectTypeRole
RTSTOREPFStore master (tax rate, till float).
RTDEPTPFDepartment master (margin target).
RTSKUPFSKU / merchandise master.
RTPRICEPFStore-level price book (STORE,SKU).
RTSTOCKPFStore stock (STORE,SKU).
RTSALE / RTSLIN / RTTENDPFPOS basket header / lines / tenders.
RTTILLPFTill reconciliation / cash-up.
RTREPLPFWeekly replenishment suggestions.
RTMKDNPFMarkdown log (before image).
RTSTKTKPFStocktake counts & variance.
RTDSUMPFDepartment sales summary.
RTGLDISTSQL tableSales/tax/cost GL distribution.
RTSTKLF / RTSLLF / RTSALFLFAlternate access paths (SKU-first, store-day).
RTSKUD / RTTXND / RTMENUDDSPFSKU inquiry / subfile inquiry / menu.
RTTILLPPRTFTill reconciliation report.
RTREFLDRPGSeed reference data.
RTPOSCP / RTPOSPTRPGLane pricing / stock depletion & post.
RTTILLR / RTTILPRRPGCash-up / print reconciliation.
RTGLPSTSQLRPGLEDaily GL distribution.
RTREPLG / RTONORDRPGReplenishment suggest+rank / raise on-order.
RTMKDNA / RTDSUMARPGMarkdown / department margin.
RTSTKTA / RTPBRLL / RTARCHRPGStocktake / roll / archive.
RTSKUXRRPGChain-wide SKU stock cross-reference (LF demo).
RTSKUIQ / RTTXNIQ / RTMENURPGOnline inquiry / subfile inquiry / menu.
RTTLRPTILE COBOLTill audit report / invariant re-proof.
RTSETUPCLCreate objects + compile programs.
RTDAILY / RTWEEK / RTMONTH / RTYEARCLThe four job cycles.

The full catalogue is 13 PFs + 3 LFs + 3 DSPFs + 1 PRTF + 1 SQL table (with index), driven by 17 RPG programs, 1 ILE COBOL program and 5 CL programs (setup + four cycles). Sections D and F expand each.

B. Online Transactions & Screens ↑ top

B.1 The command/entry line

BAZAAR/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 BAZAAR — the tested jobs run with LIBL = QSYS QGPL BAZAAR QTEMP and CURLIB = BAZAAR. (The cycle CLs also issue their own ADDLIBLE LIB(BAZAAR) defensively.)

To do thisType on the command line
Open the operator main menuCALL BAZAAR/RTMENU
SKU price / stock inquiry (direct)CALL BAZAAR/RTSKUIQ
Transaction (basket) inquiry (direct)CALL BAZAAR/RTTXNIQ
Create objects & compile everything (once)CALL BAZAAR/RTSETUP
Seed reference data (once, after setup)CALL BAZAAR/RTREFLD
Run the daily POS cycleCALL BAZAAR/RTDAILY (or SBMJOB it)
Run the weekly / monthly / annual cycleCALL BAZAAR/RTWEEK / RTMONTH / RTYEAR

The batch cycles take no CALL parameters. Rather than a control table, each individual program derives its own scope from the data or from a compile-time period literal (see the honest note in C.2): the GL post finds the earliest undistributed trading day; the weekly run stamps run number 202632; the monthly summary uses period 202608; the annual roll targets effective date 20270101. Only RTMENU, RTSKUIQ and RTTXNIQ are interactive; every batch program runs to completion and DSPLYs a one-line result.

B.2 The menu & inquiry screens (RTMENU / RTSKUIQ / RTTXNIQ)

Three programs are interactive, driven by three DDS display files:

Main menu — RTMENU / RTMENUD

A plain menu with two options; it CALLs the chosen inquiry program-to-program and returns. MOPT='1' calls RTSKUIQ, MOPT='2' calls RTTXNIQ, any other non-blank value shows Invalid option. F3 (CA03) ends the program.

SKU price / stock inquiry — RTSKUIQ / RTSKUD

A plain (non-subfile) screen. Key a store and a SKU, Enter, and it renders the SKU description and department (from RTSKU), the shelf price and promotion (from the RTPRICE row keyed on both STORE and SKU) and the store stock (from RTSTOCK). Two figures are derived on the screen and stored nowhere: the promotional price (price − half-up(price × promopc / 100)) and the available quantity (onhand − resvqty). A SKU not found, or a real SKU the store does not carry, shows SKU not found at that store. and clears the detail.

SKU Price Inquiry - BAZAAR/i Store . . : ST01 SKU . : SK000101 Description . : MERINO CREW JUMPER Department . . : APP Shelf price . : 12.99 Promotion % . : 10.00 Promo price . : 11.69 On hand . . . : 47.00 Committed . . : 0.00 Available . . : 47.00 SKU found. F3=Exit Enter=Inquire

The same SKU keyed for ST02 renders the same 12.99 shelf but a 0.00 promotion (so the promo price is the shelf price) and the ST02 book of 30 — proving the screen reads the (STORE, SKU) composite key, not the SKU alone.

Transaction inquiry — RTTXNIQ / RTTXND (subfile)

RTTXNIQ is the app's subfile screen. Key a transaction number, Enter, and it renders the basket header (store, type, net, total from RTSALE) and loads the basket's sale lines into a subfile (DDS record TSFL under control record TCTL, SFLPAG(5) per page, SFLSIZ(20), ROLLUP/ROLLDOWN paging). Each subfile row shows sequence, SKU, quantity, price, discount and net.

Transaction Inquiry - BAZAAR/i Transaction: TX000001 Store . . . . : ST01 Type . . . . . : S Net . . . . . : 61.92 Total . . . . : 67.03 Seq SKU Qty Price Disc Net 1 SK000101 3.00 12.99 3.90 35.07 2 SK000201 1.00 7.25 0.00 7.25 3 SK000301 2.00 9.80 0.00 19.60 Transaction found. F3=Exit Roll=Page Enter=Inquire
On a "not found", RTTXNIQ must not leave the previous basket's lines showing behind the message. This environment does not honour SFLCLR on a control-format WRITE once the subfile is populated, so the program remembers how many rows the last enquiry loaded (WPREV) and overwrites exactly those rows with blanks — a deterministic clear that needs no keyword support. This is an engine-compatibility accommodation documented in the source; operationally the screen behaves as described (an unknown basket reports not found and shows zero lines).

B.3 Controls & audit model (no maker–checker; controls are variance + guards)

Honest statement: BAZAAR/i does not model a four-eyes maker–checker / separate-authorization workflow, and it has no user-level audit trail table. There is no "one clerk captures, a supervisor approves" gate, and no *AUDIT-style log of who did what. The control posture the application does have is built into the batch logic:

In sum, the control model is variance reporting + balancing invariants + idempotency guards, enforced in the batch programs, rather than a segregation-of-duties approval workflow or a user-audit trail.

C. Batch Jobs & the Periodic Cycle ↑ top

BAZAAR/i's processing runs as four periodic cycles, each a CL that CALLs its programs in a fixed order: a daily cycle (price, deplete, cash-up, print, GL, audit), a weekly cycle (replenishment + raise on-order), a monthly cycle (markdown + department margin) and an annual cycle (stocktake, price-book roll, archive). All four take no CALL parameters. This is the SBMJOB idiom — a scheduled submission is a bare CALL:

-- one-time build, then seed the reference data
CALL PGM(BAZAAR/RTSETUP)
CALL PGM(BAZAAR/RTREFLD)

-- then submit a (parameterless) cycle
SBMJOB CMD(CALL PGM(BAZAAR/RTDAILY)) JOB(RTDAILY)

C.1 Full batch program set

ProgramCyclePurposeReadsWritesDSPLY result
RTPOSCPDailyPrice captured baskets off the store price book; total incl. store tax.RTSALE(C), RTSLIN, RTPRICE, RTSTORE, RTSKURTSLIN, RTSALE (line/hdr money)RTPOSCP PRICED=n SKIP=n
RTPOSPTDailyDeplete store stock line by line; flip basket to posted.RTSALE(C), RTSLIN, RTSTOCKRTSTOCK, RTSALE (TXSTAT='P')RTPOSPT POSTED=n LINES=n SKIP=n
RTTILLRDailyCash up each (store,date,till) against its tenders; record variance.RTSALE, RTSALF, RTTEND, RTSTORERTTILLRTTILLR TILLS=n SKIP=n VAR=v
RTTILPRDailyPrint the till reconciliation report (PRTF, page overflow).RTTILLspool (RTTILLP)RTTILPR TILLS=n VARIANCE=v
RTGLPSTDailyDistribute the day's sales/tax/cost into the SQL GL (balanced DR/CR).RTSALE(P), RTSLIN, RTSTORERTGLDIST (SQL)RTGLPST BATCH=d ROWS=n
RTTLRPTDailyILE COBOL: re-prove the till invariant; count out-of-balance tills.RTTILL— (display only)RTTLRPT TILLS/TENDER/SALES/VARIANCE/OUTOFBAL
RTREPLGWeeklySuggest an order for each store/SKU at/below reorder point; rank movers.RTSTOCK, RTSKU, RTSLIN, RTSALERTREPLRTREPLG RUN=r SUGGESTED=n SKIP=n
RTONORDWeeklyRaise the suggested quantities onto the store on-order book.RTREPL, RTSTOCKRTSTOCK, RTREPL (RPMSG='RD')RTONORD RAISED=n SKIP=n
RTMKDNAMonthlyBurn a permanent markdown into the price book (discontinued 30%, class C 15%).RTPRICE, RTSKURTPRICE, RTMKDNRTMKDNA MARKED=n SKIP=n
RTDSUMAMonthlySummarise sales/cost/margin by store & department; grade vs target.RTDEPT, RTSTORE, RTSLIN, RTSALE, RTSKURTDSUMRTDSUMA PER=p ROWS=n SKIP=n
RTSTKTAAnnualVariance counted stocktake against book; correct the book.RTSTKTK, RTSTOCKRTSTKTK, RTSTOCKRTSTKTA COUNTED=n SKIP=n VAR=v
RTPBRLLAnnualRoll the price book: clear promotions/markdowns, new effective date, close discontinued rows.RTPRICE, RTSKURTPRICERTPBRLL ROLLED=n CLOSED=n SKIP=n
RTARCHAnnualArchive posted sales (TXSTAT='A'); re-value store stock at SKU cost.RTSALE(P), RTSTOCK, RTSKURTSALE, RTSTOCK (SKVAL)RTARCH ARCHIVED=n VALUED=n SKIP=n

RTREFLD (seed) and RTSKUXR (chain-wide SKU cross-reference demo) are on-demand utilities, not part of any cycle.

C.2 Daily / weekly / monthly / annual detail

Daily — RTDAILY

Runs RTPOSCPRTPOSPTRTTILLRRTTILPRRTGLPSTRTTLRPT. Pricing is per line: gross = qty × price, disc = half-up(gross × promopc / 100), net = gross − disc; the header sums the line nets, then tax = half-up(net × store taxpc / 100) and total = net + tax. A return carries negative quantities, so every figure comes out negative through the same arithmetic — no separate refund path.

Expected DSPLY (rt_daily oracle: ST01 tax 8.25%, three baskets):
  RTPOSCP PRICED=3 SKIP=0
  RTPOSPT POSTED=3 LINES=4 SKIP=0    stock 50-3+1=48, 8-1=7, 100-2=98
  RTTILLR TILLS=1 SKIP=0 VAR=200     200.00 float left in drawer, TLSTAT='V'
  RTGLPST BATCH=20260801 ROWS=5      DR cash 54.38 = CR sale 50.23 + tax 4.15; COS pair 30.50
Honest note on the GL batch date. RTGLPST derives its batch as the earliest posted TXDATE not yet present in RTGLDIST — so each trading day is distributed exactly once under its own batch, and a re-run with nothing new reports RTGLPST BATCH=0 ROWS=0 NOTHING TO POST. This replaced an earlier compile-time literal (20260801) that froze the GL permanently after day one (measured: GL stuck at 588.01 while the register grew to 1157.53). The fix is in the shipped source.

Weekly — RTWEEK

Runs RTREPLG then RTONORD, under run number 202632. For each active store/SKU: available = onhand − resvqty; when available ≤ REORDP, suggest REORDQ − available − onorder floored at zero, and set the message OR (order) or OS (out of stock, when available ≤ 0). The mover rank off the week's posted units is F (≥10), M (≥3) else S. Discontinued SKUs are skipped. RTONORD then adds each suggestion to ONORDER and flips RPMSG to RD (raised).

Expected DSPLY (rt_cycles oracle: 9 stock rows, 1 discontinued):
  RTREPLG RUN=202632 SUGGESTED=3 SKIP=1  8 planned, 3 need ordering (53, 9, 84 units)
  RTONORD RAISED=3 SKIP=...              ST01 plates on-order 53, ST02 coffee 84

Monthly — RTMONTH

Runs RTMKDNA then RTDSUMA, over period 202608. Markdown ladder: discontinued SKUs take 30%, class-C slow lines take 15%, everything else nothing; new = half-up(old × (100 − pc) / 100), logged with a before image and burned into the price book. The department summary accumulates posted sale lines by (store, dept): margin = sales − cost, margin% = half-up(margin × 100 / sales) (zero sales → 0), graded P when margin% ≥ the department target else F.

Expected DSPLY (rt_cycles oracle):
  RTMKDNA MARKED=1 SKIP=...    sea salt 4.40 -> half-up(3.08) = 3.08, reason 'D'
  RTDSUMA PER=202608 ROWS=6    2 stores x 3 depts; ST02/APP 49.96% misses 50% -> F

Annual — RTYEAR

Runs RTSTKTARTPBRLLRTARCH. Stocktake varies the counted quantity against the book (CTVAR = count − book), records it and corrects the book to the count. The roll moves every price row to effective 20270101, clears PROMOPC/PRMKDN, and closes (PRSTAT='X') the price rows of discontinued SKUs. The archive flips posted baskets to TXSTAT='A' and re-values each stock row at SKU cost (SKVAL = onhand × cost).

Expected DSPLY (rt_cycles oracle: counts seeded short/over):
  RTSTKTA COUNTED=2 SKIP=0 VAR=-2   ST01 45 vs 48 = -3 (shrinkage); ST02 19 vs 18 = +1
  RTPBRLL ROLLED=9 CLOSED=1 SKIP=0  all 9 rows to 20270101; 1 discontinued closed
  RTARCH ARCHIVED=5 VALUED=9 SKIP=0 ST01 jumpers 45 x 6.50 = 292.50 valuation

C.3 Ordering & dependencies

D. Data Files (data dictionary) ↑ top

All files are in library BAZAAR, grounded in src/sources.mjs. Money is packed decimal (P); dates are stored as zoned 8S 0 in YYYYMMDD form (or YYYYMM/YYYYWW periods); tax and margin percentages are packed 5P 2 (e.g. 8.25) so a rate survives exactly.

RTSTORE — Store master (K STORE, UNIQUE)

FieldTypeMeaning
STORE4AStore code (key), e.g. ST01.
STNAME30AStore name.
STREGN3ARegion.
TAXPC5P 2Store sales-tax rate (8.25 / 6.00 seeded).
STCASH9P 2Till opening float (backed out at cash-up).
STSTAT1AA active.

RTDEPT — Department master (K DEPT, UNIQUE)

FieldTypeMeaning
DEPT3ADepartment code (key): APP / HOM / GRO.
DPNAME25ADepartment name.
MARGTG5P 2Target gross-margin % (50 / 40 / 20 seeded).
DPSTAT1AA active.

RTSKU — SKU / merchandise master (K SKU, UNIQUE)

FieldTypeMeaning
SKU8ASKU number (key), e.g. SK000101.
SKDESC30ADescription.
DEPT3AOwning department.
RETAIL / COST9P 2Chain list price / unit cost.
SKUOM3AUnit of measure (EA).
REORDP / REORDQ7P 0Reorder point / order-up-to quantity.
SKCLS1AMovement class A/B/C (C is a markdown trigger).
SKSTAT1AA active, D discontinued.

RTPRICE — Store-level price book (K STORE, K SKU, UNIQUE)

FieldTypeMeaning
STORE / SKU4A / 8AComposite key — a price per SKU per store.
PRICE9P 2Shelf price (what POS charges).
PROMOPC5P 2Promotion % off the shelf price.
PRMKDN5P 2Permanent markdown % burned into the shelf price.
PREFFDT8S 0Effective date (rolled annually).
PRSTAT1AA active, X closed (roll closes discontinued rows).

RTSTOCK — Store stock (K STORE, K SKU, UNIQUE)

FieldTypeMeaning
STORE / SKU4A / 8AComposite key.
ONHAND9P 2Sellable book quantity (floored at zero on oversell).
ONORDER9P 2Requested but not received (raised by RTONORD).
RESVQTY9P 2Reserved / laid-away.
LSTSOLD8S 0Last-sold date (stamped by RTPOSPT).
SKVAL11P 2Stock valuation (set annually by RTARCH).

RTSALE / RTSLIN / RTTEND — POS basket

FieldTypeMeaning
RTSALE.TXNO8ABasket number (key).
RTSALE.STORE / TXDATE / TXTILL4A / 8S / 2AStore, trading date, till.
RTSALE.TXTYPE1AS sale, R return.
RTSALE.TXGROSS/TXDISC/TXNET/TXTAX/TXTOTAL9P 2Basket money (set by RTPOSCP).
RTSALE.TXSTAT1AC captured, P posted, V voided, A archived.
RTSALE.TXORIG8AOriginal basket for a return.
RTSLIN.TXNO / LNSEQ8A / 3SLine key.
RTSLIN.SKU / LNQTY8A / 7P 2SKU / signed quantity (return is negative).
RTSLIN.LNPRICE/LNDISC/LNNET/LNCOST9P 2Line money (derived by RTPOSCP).
RTTEND.TXNO / TNSEQ8A / 3STender key.
RTTEND.TNTYPE1AC cash, K card, V voucher.
RTTEND.TNAMT / TNREF9P 2 / 12AAmount / reference (a basket can carry several).

RTTILL — Till reconciliation (K STORE, K TLDATE, K TILL, UNIQUE)

FieldTypeMeaning
STORE / TLDATE / TILL4A / 8S / 2AComposite key (one row per till per day).
EXPCASH / EXPCARD / EXPVOUC9P 2Expected tender by type (off RTTEND).
DECCASH / DECCARD9P 2Declared (counted) cash / card.
TLSALES9P 2Day's posted transaction totals for the till.
VARCASH9P 2Declared − expected cash (reported, never absorbed).
TLSTAT1AB balanced, V out of balance (variance ≠ 0).

RTGLDIST — GL distribution (SQL table, PK GDSEQ; index on GDACCT,GDDRCR)

FieldTypeMeaning
GDSEQDECIMAL(8,0)Distribution sequence (PK; disjoint block per store).
GDBATCHDECIMAL(8,0)Trading day this row distributes.
GDSTORE / GDACCTCHAR(4) / CHAR(9)Store / GL account (1010-CASH, 4010-SALE, 2200-TAX, 5010-COS, 1400-STK).
GDDRCRCHAR(1)D debit, C credit.
GDAMT / GDREF / GDDTDECIMAL(11,2)/CHAR(8)/DECIMAL(8,0)Amount / reference / date.

RTREPL — Replenishment suggestions (K RUNNO, K STORE, K SKU, UNIQUE)

FieldTypeMeaning
RUNNO6S 0Weekly run number (e.g. 202632); re-run of the same is refused.
STORE / SKU4A / 8AStore / SKU.
RPONHND / RPSOLD / RPSUG9P 2On-hand / week units sold / suggested order.
RPRANK1AMover rank F/M/S.
RPMSG2AOR order, OS out-of-stock, RD raised, blank none.

RTMKDN — Markdown log (K MKSEQ, UNIQUE)

FieldTypeMeaning
MKSEQ8S 0Stable markdown key (SKU digits + store ordinal, disjoint range).
STORE / SKU4A / 8AStore / SKU.
OLDPRC / NEWPRC9P 2Before image / new shelf price.
MKPC / MKDT5P 2 / 8S 0Markdown % / date.
MKRSN1AD discontinued, S slow (class C).

RTSTKTK — Stocktake counts (K CTSEQ, UNIQUE)

FieldTypeMeaning
CTSEQ8S 0Count sequence (key).
STORE / SKU4A / 8AStore / SKU.
CNTQTY / BOOKQTY / CTVAR9P 2Counted / book / variance (count − book).
CTDT / CTSTAT8S 0 / 1ACount date / O open, P processed.

RTDSUM — Department sales summary (K DSPER, K STORE, K DEPT, UNIQUE)

FieldTypeMeaning
DSPER6S 0Period YYYYMM (e.g. 202608); re-close refused.
STORE / DEPT4A / 3AStore / department.
DSSALES / DSCOST / DSMARG11P 2Net sales / cost / margin.
DSMGPC5P 2Margin % (half-up).
DSUNITS9P 2Units sold.
DSGRADE1AP pass (≥ target), F fail.

Logical files (alternate access paths)

Seeded reference data (RTREFLD)

2 stores (ST01 tax 8.25% float 200.00; ST02 tax 6.00% float 150.00), 3 departments (APP 50% / HOM 40% / GRO 20%), 6 SKUs across them (one discontinued: SK000302), 9 price-book rows (ST01 runs a 10% jumper promotion; every other price equals the SKU retail so the promo arithmetic is the only variable), and 9 opening store-stock rows.

E. Operations Runbook ↑ top

E.1 Day-in-the-life

  1. Confirm the job's library list includes BAZAAR and that the reference data is seeded (once: RTSETUP then RTREFLD).
  2. Confirm the lane has written the day's captured baskets (TXSTAT='C') into RTSALE / RTSLIN / RTTEND.
  3. Submit the daily cycle: SBMJOB CMD(CALL PGM(BAZAAR/RTDAILY)).
  4. Post-check the daily run (see below), then handle inquiries via CALL BAZAAR/RTMENU.
  5. If today is a weekly replenishment day, submit RTWEEK and review the RTREPL suggestions and the on-order raises.

Post-checks after the daily cycle (reconciling figures — the same ones the rt_daily volume driver checks against an independent JS oracle):

E.2 Period-end close

  1. Confirm the month's daily cycles have all posted (every basket for the period is P).
  2. Submit RTMONTH. Confirm RTMKDNA MARKED= and RTDSUMA ROWS=, then reconcile:
SELECT DSPER, STORE, DEPT, DSSALES, DSCOST, DSMARG, DSMGPC, DSGRADE
  FROM BAZAAR.RTDSUM WHERE DSPER = 202608;

For the annual close, load the physical counts into RTSTKTK (CTSTAT='O') then submit RTYEAR; reconcile the stocktake net variance, confirm all price rows carry the new effective date (20270101) with promotions/markdowns cleared and the discontinued row closed (PRSTAT='X'), and confirm every posted basket is archived (TXSTAT='A') with each stock row valued at cost.

E.3 Failure & re-run rules

Each program DSPLYs its result. Every cycle in BAZAAR/i is re-runnable — the rt_daily and rt_cycles drivers run each cycle two and three times and prove the books do not move. The guards are status flips and stable business keys:

SituationBehaviourAction
Re-run RTPOSPT / RTPOSCPOnly TXSTAT='C' baskets are priced/posted; the flip to P is the guard.Safe. A re-run posts 0 and stock does not double-deplete (still 48, not 45).
Re-run RTTILLRThe (STORE,DATE,TILL) key CHAIN(EN) guard refuses a second cash-up.Safe. TILLS=0; still exactly one reconciliation row.
Re-run RTGLPST same dayBatch = earliest undistributed day; nothing new to post.Safe. BATCH=0 ROWS=0 NOTHING TO POST; a genuinely new trading day IS distributed under its own batch.
Crash mid-basket (post)The CP flip is written AFTER the line work.Re-run RTPOSPT: the unposted basket re-posts cleanly rather than half-posting.
Re-run RTWEEK(RUNNO,STORE,SKU) guard; on-order only raised for un-raised suggestions.Safe. SUGGESTED=0 / RAISED=0; on-order does not compound (still 53, not 106).
Re-run RTMONTHMarkdown key + (PER,STORE,DEPT) summary key guards.Safe. MARKED=0 / ROWS=0; the price does not compound down (still 3.08).
Re-run RTYEARCTSTAT='P', PREFFDT≥new-date and TXSTAT≠'P' skip already-processed rows.Safe. COUNTED=0 / ROLLED=0 / ARCHIVED=0; the book is not corrected twice.
OversellOn-hand floors at zero, shortfall counted in the run's oversell total.No negative book anywhere; investigate the counted shortfall and adjust stock.
Because every basket carries its own lines and tenders, every money movement lands in a keyed file (RTSLIN, RTTEND, RTTILL, RTGLDIST), and every markdown logs a before image, any cycle's effect is reconstructable after the fact for reconciliation and recovery.

F. Developer Reference ↑ top

Implementation detail from src/sources.mjs. All objects are in library BAZAAR. The RPG is written column-exact fixed-form (the C()/D() helpers emit C- and D-specs) with /free blocks for the arithmetic.

F.1 Arithmetic conventions

F.2 Access paths & the LFs

The three logical files are genuine alternate access paths, not decoration. RTSKUXR demonstrates it: it opens RTSTKLF (keyed SKU-first) and does a partial keyed READE on the SKU alone — a read sequence the physical file (STORE-first) cannot do without a full scan. The build oracle: 9 stock rows over 6 SKUs, SK000101 carried by both stores, 268 total units (RTSKUXR SKUS=6 ROWS=9 MULTI=2 UNITS=268). Because chains move the file cursor, the batch programs use explicit KLISTs and re-CHAIN the header/line after each lookup (documented inline as "re-CHAIN ... the cursor moved").

F.3 Idempotency guards

ProgramGuard
RTPOSCP / RTPOSPTOnly TXSTAT='C' processed; RTPOSPT flips to P after the line work.
RTTILLRCHAIN(EN) on the (STORE,TLDATE,TILL) key + LEAVESR.
RTGLPSTBatch = MIN(TXDATE) posted and not already in RTGLDIST; GDSEQ continues past MAX(GDSEQ).
RTREPLGCHAIN(EN) on the (RUNNO,STORE,SKU) key + LEAVESR.
RTONORDProcesses only RPMSG OR/OS with RPSUG>0; flips RPMSG to RD.
RTMKDNACHAIN(EN) on the stable MKSEQ key + LEAVESR; also skips when new price = old.
RTDSUMACHAIN(EN) on the (DSPER,STORE,DEPT) key + LEAVESR.
RTSTKTASkips CTSTAT='P' count rows.
RTPBRLLSkips rows already at PREFFDT ≥ 20270101.
RTARCHOnly TXSTAT='P' baskets are archived.

F.4 The SQLRPGLE GL post (RTGLPST)

RTGLPST is the one SQLRPGLE program. It selects the batch date and the current max sequence with EXEC SQL SELECT ... INTO, accumulates each store's posted day (net, tax, total, cost of sale) with RPG file reads, and inserts balanced rows with free-form embedded SQL:

-- batch = earliest posted day not yet distributed
exec sql select coalesce(min(TXDATE),0) into :wbatch
  from BAZAAR.RTSALE where TXSTAT='P'
    and TXDATE not in (select distinct GDBATCH from BAZAAR.RTGLDIST);

-- one balanced insert per GL leg
exec sql insert into BAZAAR.RTGLDIST
  values (:wseq,:wbatch,:wst,:wacct,:wdc,:wamt,:wref,:wbatch);

Accounts: DR 1010-CASH = TXTOTAL, CR 4010-SALE = TXNET, CR 2200-TAX = TXTAX; DR 5010-COS / CR 1400-STK = Σ(line cost × qty). A disjoint 10-sequence block per store keeps a later trading day from colliding on the primary key.

F.5 DSPLY result-line table

Every program ends with a single DSPLY the operator (and the tests) read back. Section C.1 lists the format of each; a healthy daily cycle reads:

RTPOSCP PRICED=3 SKIP=0
RTPOSPT POSTED=3 LINES=4 SKIP=0
RTTILLR TILLS=1 SKIP=0 VAR=200
RTTILPR TILLS=1 VARIANCE=200
RTGLPST BATCH=20260801 ROWS=5
RTTLRPT TILLS 1.00 / TENDER 54.38 / SALES 54.38 / VARIANCE 200.00 / OUTOFBAL 1.00

G. Glossary ↑ top

Basket (transaction)
One POS sale or return: a header (RTSALE) plus its lines (RTSLIN) and tenders (RTTEND). Captured 'C', posted 'P', voided 'V', archived 'A'.
Cash-up / till reconciliation
Reconciling a till's declared cash to the expected tender for the day (RTTILLR), reporting any variance (VARCASH) rather than absorbing it.
Cost of sale (COS)
The SKU cost of what was sold (line cost × quantity), distributed DR 5010-COS / CR 1400-STK.
DSPLY
The RPG/COBOL operation that writes a one-line message; every BAZAAR/i program ends with a result DSPLY the operator reads back (section F.5).
GL distribution
Turning a day's posted trade into balanced debit/credit rows in the GL table (RTGLPST / RTGLDIST).
Half-up rounding
Rounding to the cent with .005 going up (via %dech). Applied to every derived money figure.
Idempotent / re-runnable
Safe to run again with no further effect. Every BAZAAR/i cycle is, guarded by status flips or stable business keys (section F.3).
Logical file (LF)
An alternate keyed access path over a physical file. RTSTKLF/RTSLLF give SKU-first paths; RTSALF gives a store+date path.
Markdown
A permanent shelf-price reduction burned into the price book (RTMKDNA / RTMKDN): discontinued 30%, class-C slow 15%.
Mover rank
A slow/medium/fast classification of a line by the week's units sold: F ≥ 10, M ≥ 3, else S.
Price book
The effective price (and promotion) for a SKU at a store (RTPRICE, keyed STORE+SKU). What POS charges, as distinct from the chain list price on the SKU master.
Replenishment suggestion
A weekly order proposal when available stock falls to the reorder point: order up to REORDQ less available less on-order (RTREPLG), raised onto the on-order book by RTONORD.
SBMJOB
Submit Job — the IBM i command that queues a program as a batch job, e.g. SBMJOB CMD(CALL PGM(BAZAAR/RTDAILY)).
Subfile
A 5250 display construct listing many rows on one screen (DDS SFL/SFLCTL). RTTXNIQ's basket-line list is a subfile.
Till invariant
EXPCASH + EXPCARD + EXPVOUC = TLSALES — the tender ledger must foot to the day's posted totals; asserted by RTTILLR and re-proved by the COBOL RTTLRPT.