PRESSCO/i is a dry-cleaning / commercial-laundry order-to-cash application: order
intake by garment × service, volume-agnostic per-garment pricing with a rush surcharge, garment-tag
tracking through a real cleaning-plant lifecycle, route delivery and counter pickup, retail pay-at-pickup
cash versus commercial on-account invoicing with credit control, AR ageing and statement print, and a
balanced revenue GL feed. The whole estate is classic IBM i: DDS physical/logical files, RPG IV batch
programs, two 5250 inquiry screens (one with a subfile), an ILE COBOL AR control report, an SQLRPGLE GL
post over a DB2 for i table, and CL job cycles. This manual is the reference for the operator who runs the
daily/weekly/monthly/annual cycles and the two online screens, and for the developer maintaining the
application. It is grounded entirely in the committed source (laundry-app/src/sources.mjs,
src/seed.mjs, and the test/pc_*.mjs drivers). Everything runs in library
PRESSCO.
PRESSCO/i services the full order-to-cash life of a dry-cleaning / commercial-laundry job:
PCSVCP service
price list, applying a rush surcharge where flagged, and stamp the order header totals and a promise
date.One field — PCORDH.OSTAT — drives the whole order through the plant and billing.
Each daily program advances the orders it is responsible for:
| OSTAT | Meaning | Set by |
|---|---|---|
| N | New — captured, not yet priced/tagged. | Intake (operator writes the header/lines). |
| P | Priced & garments tagged, in the cleaning flow. | PCPRICE (then PCTAGGEN tags). |
| R | Ready — every garment tag reached READY. | PCFLOW (lifts the header once all tags are R). |
| D | Delivered / collected. | PCROUTED (routed) or PCCOUNTER (counter). |
| H | Delivered but held at billing — credit limit, manual hold, or inactive customer. | PCINVGEN (retried on later runs). |
| B | Billed — commercial invoice raised, or retail receipt taken. | PCINVGEN (commercial) / PCCASHR (retail). |
| X | Cancelled. | Manual (a cancelled line carries LSTAT='X'). |
Credit control bites at billing, not at intake. Dry-cleaning is a service already rendered by the
time an order reaches D (the garments are already back from the plant and out the door), so
there is no allocation stage to block. A commercial order that would breach its customer's limit, or belongs
to a held / inactive account, is instead held at billing: OSTAT flips to H
with an OHOLDR reason and neither an invoice nor a balance movement occurs. A later run
re-tries a held order once the account clears, so a hold is never a dead end.
ONLINE (5250) DAILY (PCDAILY) PERIODIC
------------- ---------------- --------
PCMENU PCPRICE price lines, stamp totals WEEKLY (PCWEEK)
+-opt1-> PCCUSTIQ PCTAGGEN issue garment tags (I) PCROUTW route-stop report
+-opt2-> PCORDIQ PCFLOW I->C->R, lift order to R MONTHLY (PCMONTH)
(subfile) PCROUTED routed stops -> D, tags O PCAGE age open items 0/1/2/3
PCCOUNTER counter orders -> D, tags O PCSTMT statement print (PRTF)
PCINVGEN commercial invoice / HOLD PCGLPST revenue GL post (DR/CR)
PCCASHR retail receipt / AR cash PCARRPT AR control report (COBOL)
ANNUAL (PCYEAR)
PCARCHIV turnover archive + reset
PCCUST PCSVCP PCROUTE <-- reference (seeded by PCREFLD)
PCORDH PCORDL PCTAG PCSTOP PCINVH PCAROP PCRCPT <-- operational files
PCHIST (durable audit trail, every posting program writes it) PCGLDIST (SQL GL)
A single order flows: intake writes PCORDH/PCORDL (OSTAT N)
→ PCPRICE prices the lines and stamps the header (P) →
PCTAGGEN issues a PCTAG per line (I) → two PCFLOW
passes walk the tags I→C→R and lift the order to R →
PCROUTED/PCCOUNTER deliver it (D, tags O) →
PCINVGEN bills or holds it, or PCCASHR takes a retail receipt (B).
Every posting stamps a PCHIST row.
| Object | Type | Role |
|---|---|---|
| PCCUST | PF | Customer master (commercial 'C' / retail 'R'). |
| PCSVCP | PF | Service price list, keyed garment×service. |
| PCORDH | PF | Order header (OSTAT lifecycle). |
| PCORDL | PF | Order lines — one per garment batch (composite key). |
| PCTAG | PF | Garment tags (TSTAT cleaning lifecycle). |
| PCTAGSTL | LF | Tags keyed by status then tag (plant worklist). |
| PCROUTE | PF | Delivery route master. |
| PCSTOP | PF | Route stops (route, stop → order). |
| PCINVH | PF | Commercial invoice header. |
| PCAROP | PF | AR open items (commercial only, composite key). |
| PCARDLF | LF | AR open items keyed by due date (oldest first). |
| PCRCPT | PF | Receipts — retail 'T' and AR-cash 'A'. |
| PCHIST | PF | Durable audit trail (unique HSEQ, disjoint ranges). |
| PCGLDIST | SQL table | Revenue/AR GL distribution (DR/CR). |
| PCCUSTD / PCORDD / PCMENUD | DSPF | Customer inquiry / order subfile / menu. |
| PCAGEP / PCROUTP | PRTF | AR ageing statement / route-stop report. |
| PCREFLD | RPGLE | Seed reference data (customers, prices, routes). |
| PCPRICE PCTAGGEN PCFLOW PCROUTED PCCOUNTER PCINVGEN PCCASHR | RPGLE | The daily order-to-cash chain. |
| PCROUTW PCAGE PCSTMT PCARCHIV | RPGLE | Weekly / monthly / annual batch. |
| PCGLPST | SQLRPGLE | Monthly revenue GL post (embedded SQL). |
| PCARRPT | CBLLE | ILE COBOL AR control report. |
| PCCUSTIQ PCORDIQ PCMENU | RPGLE | Online inquiry programs. |
| PCSETUP PCDAILY PCWEEK PCMONTH PCYEAR | CLP | Build + the four job cycles. |
The full catalogue is 11 physical files + 2 logical files + 1 SQL table, 5 DDS display/printer files, 15 RPG/SQLRPGLE programs, 1 COBOL program and 5 CL programs. Sections D and F expand each.
PRESSCO/i has no CICS transaction identifiers. 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 PRESSCO — the tested jobs run with
LIBL = QSYS QGPL PRESSCO QTEMP and CURLIB = PRESSCO.
| To do this | Type on the command line |
|---|---|
| Open the main menu (routes to both inquiries) | CALL PRESSCO/PCMENU |
| Customer inquiry directly | CALL PRESSCO/PCCUSTIQ |
| Order/garment inquiry directly | CALL PRESSCO/PCORDIQ |
| Build every object (once, at install) | CALL PRESSCO/PCSETUP |
| Load the seeded reference data | CALL PRESSCO/PCREFLD |
| Run the daily order-to-cash cycle | CALL PRESSCO/PCDAILY (or SBMJOB it) |
| Run the weekly / monthly / annual cycle | CALL PRESSCO/PCWEEK / PCMONTH / PCYEAR |
Only the three inquiry programs are interactive; every batch program takes no CALL parameters
and DSPLYs a one-line summary per step. There is no online order-entry screen — orders are captured
by writing PCORDH/PCORDL rows (the test drivers do exactly this), then the daily
chain prices, tracks, delivers and bills them.
PCMENU (over DDS PCMENUD) is a two-option driver: option 1 CALLs
PCCUSTIQ, option 2 CALLs PCORDIQ; any other key sets "Invalid option.";
F3 ends the program.
PCCUSTIQ (over PCCUSTD, a plain non-subfile screen) reads a customer number and
shows the master record plus the derived available credit (limit less balance) for a commercial
account. A miss reports "Customer not found." and clears the detail rather than showing stale values.
| Field | Type (DDS) | Shows |
|---|---|---|
| ICUST | 6A input/output | Customer number keyed in. |
| DNAME | 30A output | Customer name (CNAME). |
| DTYPE | 1A output | C commercial / R retail. |
| DLIMIT / DBAL | 14A output | Credit limit / current balance (edited from packed 11P2). |
| DAVAIL | 14A output | Derived: CRLIMIT - BALANCE. |
| DHOLD | 1A output | Manual-hold flag (CHOLD). |
| DMSG | 50A output | "Customer found." / "Customer not found." |
PCORDIQ is the app's subfile screen. It reads an order number, shows the header (customer,
status, hold reason, route/stop, total), and loads a subfile (DDS record OSFL under
control record OCTL, SFLPAG(5) per page, SFLSIZ(20)) of the order's
garment lines with each line's tag status. It clears and reloads the subfile on every pass, and
drives SFLDSP from an indicator so the subfile is displayed only once at least one row has
loaded — leaving SFLDSP unconditional would redisplay stale rows on a miss (ORDERIS/i
precedent, noted in the source).
| Field | Type (DDS) | Shows |
|---|---|---|
| SSEQ | 2Y,0 output | Line sequence (LSEQ). |
| SGARM / SSVC | 5A output | Garment type / service type. |
| SQTY | 7A output | Line quantity (edited). |
| SNET | 12A output | Extended net (LNET, edited). |
| SSTAT | 1A output | Line status: O open, P priced/tagged, X cancelled. |
IORDNO — key an order number, Enter to inquire.DOROUTE — shows OROUTE + '/' + OSTOPSQ (blank route = counter pickup).DOTOT — the priced order total (OTOTVAL).Honest statement: PRESSCO/i does not model a true four-eyes maker–checker / separate-authorization workflow. The two online programs are read-only inquiries — they never update a file. Every posting happens in the unattended batch chain. The control model the application does have is:
T), flow advance (F), delivery/collection (D),
invoice raised (I), cash (C), annual archive (Y). Each row's
key is derived from a stable business key in a disjoint numeric range (see F.3), so the trail is
complete and reconstructable and cannot collide across programs.PCINVGEN refuses to invoice an order that would breach
the customer's available credit (CL), or belongs to a manually-held (MH) or
inactive (CS) customer; the order is held (OSTAT='H') with no money movement
and re-tried on later runs.PCROUTED
delivers only R orders on a pending stop; PCCASHR's retail path settles only
a D order). Anything out of scope is skipped and counted in the SKIP= total.In sum, the control posture is audit + credit control + status gating + a strict AR invariant, enforced by the batch programs, rather than a segregation-of-duties approval workflow.
PRESSCO/i runs as four CL-driven cycles: a daily order-to-cash chain (PCDAILY, seven
programs), a weekly route-stop report (PCWEEK), a monthly AR close
(PCMONTH, four programs) and an annual archive/reset (PCYEAR). Every
program takes no CALL parameters; run dates are constants inside the source (the monthly ageing uses
a run date of 20260930, the GL batch 202609). A scheduled submission is a bare
SBMJOB CMD(CALL PGM(PRESSCO/PCDAILY)).
| Program | Cycle | Purpose | Reads | Writes / effect | DSPLY summary |
|---|---|---|---|---|---|
| PCPRICE | daily | Price every N order's lines, stamp header totals + promise date. | PCORDH, PCORDL, PCSVCP | PCORDL (LUNITP/LNET/LSTAT=P), PCORDH (totals, OSTAT=P). | PCPRICE PRICED=n SKIP=n |
| PCTAGGEN | daily | Issue one garment tag per line of every P order. | PCORDH, PCORDL | PCTAG (TSTAT=I), PCHIST (T). | PCTAGGEN TAGGED=n SKIP=n |
| PCFLOW | daily | Advance every tag one stage (I→C, C→R); lift a fully-ready order to R. | PCTAG, PCORDH | PCTAG (advanced), PCORDH (OSTAT=R), PCHIST (F). | PCFLOW ADVANCED=n |
| PCROUTED | daily | Deliver every R order on a pending route stop. | PCSTOP, PCORDH, PCTAG | PCORDH (D), PCTAG (O), PCSTOP (RSTOPST=D), PCHIST (D). | PCROUTED DELIVERED=n SKIP=n |
| PCCOUNTER | daily | Collect every R order with no route (counter pickup). | PCORDH, PCTAG | PCORDH (D), PCTAG (O), PCHIST (D). | PCCOUNTER COLLECTED=n SKIP=n |
| PCINVGEN | daily | Invoice every delivered commercial order, or hold it on credit control. | PCORDH, PCCUST | PCINVH, PCAROP, PCHIST (I), PCCUST (balance +total), PCORDH (B or H). | PCINVGEN INVOICED=n HELD=n SKIP=n |
| PCCASHR | daily | Settle unapplied receipts: retail 'T' (order→B) and AR 'A' (reduce balance/open item). | PCRCPT, PCORDH, PCAROP, PCCUST, PCINVH | PCRCPT (RCAPPL/RCSTAT), balances, PCHIST (C). | PCCASHR APPLIED=n SKIP=n |
| PCROUTW | weekly | Print every route stop and its status (PRTF). | PCSTOP | PCROUTP report. | PCROUTW STOPS=n |
| PCAGE | monthly | Stamp each open item's ageing bucket 0/1/2/3 from days overdue. | PCAROP (via PCARDLF) | PCAROP (ARAGE). | PCAGE B0=n B1=n B2=n B3=n |
| PCSTMT | monthly | Print the AR ageing statement, oldest-due-first (PRTF). | PCAROP (via PCARDLF), PCCUST | PCAGEP report. | PCSTMT LINES=n OPEN=n |
| PCGLPST | monthly | Post the balanced revenue GL movement (DR AR / CR revenue). | PCINVH, PCGLDIST | PCGLDIST (DR/CR pair). | PCGLPST BATCH=n ROWS=n |
| PCARRPT | monthly | COBOL AR control report reconciling the open-item ledger. | PCAROP | DSPLY totals. | PCARRPT RAISED/OPEN/ITEMS/OVERDUE |
| PCARCHIV | annual | Write a per-customer turnover archive row; close settled AR items. | PCCUST, PCHIST, PCAROP | PCHIST (Y), PCAROP (ARSTAT=C). | PCARCHIV ARCHIVED=n CLOSED=n SKIP=n |
PCDAILY CALLs the seven daily programs in a fixed order:
PCPRICE → PCTAGGEN → PCFLOW → PCROUTED → PCCOUNTER → PCINVGEN →
PCCASHR, then sends the banner "PRESSCO daily order-to-cash cycle complete."
The cleaning plant advances one stage per run. PCFLOW moves every tag exactly one
step (I→C, then C→R) from its persisted status, so a brand-new order reaches READY only
on its second daily run — day 1 tags it and moves it to cleaning, day 2 moves it to ready and
lifts the header to R, at which point delivery and billing fire. This is real plant behaviour:
a same-day rush order still needs the plant's consecutive daily passes to reach ready inside its promised
window.
Worked example (from test/pc_daily.mjs, four seeded orders): ORD00001 K00001 commercial, route RT01/1: 4 x SHIRT/WASH @ 3.50 = 14.00 2 x SUIT2/DRYCL rush @ 12.00 +50% = 36.00 -> order total 50.00 promise date = 20260801 + max(turn 2,3) = 20260804 ORD00002 K00002 commercial limit 150.00, counter: 30 x COAT/PRESS rush @ 8.00 +25% = 300.00 -> HELD 'CL' (300 > 150) ORD00003 K00003 commercial, manual hold, counter: 5 x TROUS/DRYCL @ 6.00 = 30.00 -> HELD 'MH' ORD00004 R00001 RETAIL, counter: 3 x DRESS/DRYCL @ 9.50 = 28.50 -> settled by retail receipt Expected DSPLY across two daily runs: PCPRICE PRICED= 4 SKIP= 0 PCTAGGEN TAGGED= 5 SKIP= 0 5 lines -> 5 tags PCFLOW ADVANCED= 5 day1 I->C ; day2 C->R + orders lifted to R PCROUTED DELIVERED= 1 SKIP= 0 the one routed stop PCCOUNTER COLLECTED= 3 SKIP= 0 the three no-route orders PCINVGEN INVOICED= 1 HELD= 2 SKIP= ... IV000001 raised; 2 held PCCASHR APPLIED= 1 SKIP= ... once a receipt is present
'IV0' + last-5 of ORDNO,
e.g. ORD00001 → IV000001), so a re-run regenerates the same key and the
CHAIN(EN) guard refuses to double-bill. The whole daily chain is safely re-runnable: balances
do not double and no duplicate invoices appear. Held orders (H) are re-tried on every run and
bill automatically once the account's balance/hold clears.Calls PCROUTW, which walks PCSTOP and prints every route stop and its status
(P pending / D done) on the PCROUTP printer file, then DSPLYs PCROUTW STOPS=n. A
stop the daily chain delivered shows D.
Calls PCAGE → PCSTMT → PCGLPST → PCARRPT. Ageing uses a 30/360 serial:
serial(yyyymmdd) = ((yyyy×12) + mm)×30 + dd, days overdue = serial(rundate) -
serial(duedate), run date 20260930. Buckets: ≤0 current (0), 1–30
(1), 31–60 (2), 61+ (3).
Worked example (from test/pc_cycles.mjs): IV000001 due 20260831 -> 29 days -> bucket 1 (the real invoice, 50.00) IV000900 due 20261015 -> not yet due -> bucket 0 IV000901 due 20260810 -> 50 days -> bucket 2 IV000902 due 20260715 -> 75 days -> bucket 3 PCAGE B0= 1 B1= 1 B2= 1 B3= 1 PCSTMT LINES= 4 OPEN= 175.00 50 + 40 + 25 + 60 PCGLPST BATCH= 202609 ROWS= 2 DR 1200-AR 50.00 / CR 4100-LNDY 50.00 PCARRPT ITEMS 4 COBOL reconciliation over the ledger
PCGLPST posts only the movement since the last close: it sums the
invoice register, subtracts what is already posted to 1200-AR / D, and writes a balanced
DR 1200-AR / CR 4100-LNDY pair only for the difference — so a second run
posts nothing further (movement-only). The three AR-only seeded items have no invoice header, so they age
and appear on the statement but never touch the GL invoice-register total. Laundry billing carries no
separate tax line in this design, so DR total = CR total exactly.Calls PCARCHIV then PCARRPT. PCARCHIV writes one turnover-archive
history row (HTYPE='Y') per customer from that customer's invoice (I)
history rows only — cash rows carry negative/non-invoice amounts and are deliberately excluded to
avoid double-counting against the gross invoiced figure. A customer that raised no invoice (e.g. a
permanently-held one) gets no archive row. Pass 2 closes off every fully-settled AR open item
(AROPEN≤0 → ARSTAT='C').
PCDAILY: price before tag (tagging
needs a priced order at P); flow before delivery (delivery needs orders at R);
delivery before billing (billing needs orders at D).PCFLOW advances only one plant
stage per run, a new order needs at least two daily runs to progress from intake through to billed.All files are in library PRESSCO, grounded in the DDS of src/sources.mjs.
Dates are stored as signed numeric YYYYMMDD (8S 0); money is packed decimal
(11P 2 unless noted); rush surcharge and prices are packed. Access paths marked UNIQUE reject
a duplicate key.
| Field | Type | Meaning |
|---|---|---|
| CUSTNO | 6A | Customer number (key). K* commercial, R* retail by convention. |
| CNAME / CADDR / CPHONE | 30A / 30A / 12A | Name, address, phone. |
| CTYPE | 1A | C commercial (billed on statement, carries AR), R retail (pay-at-pickup, no AR ever). |
| CRLIMIT | 11P 2 | Credit limit — meaningful only for CTYPE 'C'. |
| BALANCE | 11P 2 | Outstanding AR balance — only ever moved for a commercial account. |
| CTERMS | 3S 0 | Payment terms in days (AR due date = invoice date + terms). |
| CHOLD | 1A | Manual hold: 'Y' blocks billing (reason 'MH'). |
| CSTAT | 1A | Account status: 'A' active (billing needs this; otherwise 'CS'). |
| CREVYR | 4S 0 | Revenue year (informational). |
Seeded (PCREFLD): K00001 REGENCY HOTEL GROUP (comm, limit 8000, terms 30); K00002 CORNER BISTRO LTD (comm, tight limit 150, terms 14); K00003 ARREARS DRY CLEANING CO (comm, hold Y); R00001 MRS J HARTLEY, R00002 MR T OKAFOR (retail).
| Field | Type | Meaning |
|---|---|---|
| GARMTYPE | 5A | Garment type (key 1): SHIRT, TROUS, SUIT2, DRESS, COAT, DUVET, CURTN, LINEN. |
| SVCTYPE | 5A | Service type (key 2): WASH, DRYCL, PRESS, ALTER. |
| PRICE | 9P 2 | Per-garment base charge. |
| RUSHPCT | 5P 2 | Rush surcharge percent applied when a line is flagged rush. |
| TURNDAYS | 2S 0 | Standard turnaround; the slowest line's turnaround sets the promise date. |
| PSTAT | 1A | Price-list status ('A' active). |
Seeded: SHIRT/WASH 3.50 rush50 2d, TROUS/DRYCL 6.00 rush50 2d, SUIT2/DRYCL 12.00 rush50 3d, DRESS/DRYCL 9.50 rush50 3d, COAT/PRESS 8.00 rush25 2d, DUVET/WASH 15.00 rush0 4d, CURTN/DRYCL 18.00 rush0 5d, LINEN/WASH 4.00 rush25 2d. This two-field UNIQUE key is the one PG-PRESSCO-001 concerns (F.4).
| Field | Type | Meaning |
|---|---|---|
| ORDNO | 8A | Order number (key), e.g. ORD00001. |
| CUSTNO | 6A | Owning customer. |
| ORDDT / PROMDT | 8S 0 | Intake date / promise date (= intake + slowest turnaround). |
| OSTAT | 1A | Lifecycle: N/P/R/D/H/B/X (see A.2). |
| OHOLDR | 2A | Hold reason when held at billing: 'CL' / 'MH' / 'CS'. |
| ONETVAL / ORUSHVAL / OTOTVAL | 11P 2 | Net value / rush portion / order total, stamped by pricing. |
| OINVNO | 8A | Invoice number once billed (commercial). |
| OROUTE / OSTOPSQ | 4A / 3S 0 | Delivery route / stop; blank OROUTE = counter pickup. |
| Field | Type | Meaning |
|---|---|---|
| ORDNO / LSEQ | 8A / 3S 0 | Order + line sequence (composite key). |
| GARMTYPE / SVCTYPE | 5A / 5A | Garment + service (looked up in PCSVCP). |
| LQTY | 5S 0 | Garments on this line. |
| LRUSH | 1A | 'Y' rush requested — surcharge applies. |
| LUNITP / LNET | 9P 2 / 11P 2 | Priced unit charge (after rush) / extended net = LQTY×LUNITP. |
| LSTAT | 1A | 'O' open/unpriced, 'P' priced/tagged, 'X' cancelled. |
Because the key is composite, every re-CHAIN of this file uses a KLIST (both ORDNO and LSEQ), never a single-field CHAIN.
| Field | Type | Meaning |
|---|---|---|
| TAGNO | 9A | Globally-unique tag number (key), derived 'T' + (order-digits×1000 + line). |
| ORDNO / LSEQ | 8A / 3S 0 | Order + line the tag belongs to. |
| GARMTYPE | 5A | Garment type. |
| TSTAT | 1A | 'I' intake, 'C' in cleaning, 'R' ready, 'O' out. |
| TDT | 8S 0 | Tag date (= order intake date). |
PCTAGSTL is a non-unique LF over the same file keyed TSTAT then
TAGNO — the plant-floor worklist (everything still I or C), walked without a full-file scan.
| File / Field | Type | Meaning |
|---|---|---|
| PCROUTE.RTNO / RTNAME / RSTAT | 4A / 20A / 1A | Route number (key), name, status. Seeded RT01 CITY CENTRE LOOP, RT02 WEST SIDE HOTELS. |
| PCSTOP (key RTNO, STOPSQ) | — | One row per (route, stop). |
| PCSTOP.ORDNO / CUSTNO | 8A / 6A | Order this stop delivers, and its customer. |
| PCSTOP.RSTOPST | 1A | 'P' pending, 'D' done. |
| Field | Type | Meaning |
|---|---|---|
| INVNO | 8A | Invoice number (key), derived from the order number. |
| ORDNO / CUSTNO | 8A / 6A | Billed order and customer. |
| INVDT | 8S 0 | Invoice date. |
| INVNET / INVTOT | 11P 2 | Net / total (equal in this design — no tax line). |
| INVSTAT | 1A | 'O' open, 'P' paid. |
| Field | Type | Meaning |
|---|---|---|
| CUSTNO / INVNO | 6A / 8A | Customer + invoice (composite key). Commercial only. |
| ARINVDT / ARDUEDT | 8S 0 | Invoice date / due date (= invoice date + terms). |
| ARAMT / AROPEN | 11P 2 | Invoiced amount / outstanding (INVTOT less cash applied). |
| ARAGE | 1S 0 | Ageing bucket 0/1/2/3, stamped by PCAGE. |
| ARSTAT | 1A | 'O' open, 'P' paid, 'C' closed (year-end). |
PCARDLF reads the same items keyed by due date, so PCAGE and PCSTMT walk the
ledger oldest-due-first with no sort.
| Field | Type | Meaning |
|---|---|---|
| RCPTNO / CUSTNO | 8A / 6A | Receipt number (key) + customer. |
| RCTYPE | 1A | 'T' retail counter (settles an order, no AR), 'A' AR application (settles an invoice open item). |
| RCORDNO / RCINVNO | 8A / 8A | Order settled (T) / invoice settled (A). |
| RCDT / RCAMT / RCAPPL | 8S 0 / 11P 2 | Date / tendered / actually applied (over-payment applies only what is owed). |
| RCSTAT | 1A | 'U' unapplied, 'A' fully applied, 'P' part-applied. |
| Field | Type | Meaning |
|---|---|---|
| HSEQ | 8S 0 | History key (key) — derived, disjoint range per program (see F.3). |
| CUSTNO / HITEM | 6A / 9A | Customer + item (tag no / 'INVOICE' / 'ROUTE' etc.). |
| HTYPE | 1A | T tag, F flow, D delivery, I invoice, C cash, Y archive. |
| HDT / HQTY / HAMT | 8S 0 / 5S 0 / 11P 2 | Date / quantity / amount (signed; cash rows negative). |
| HREF / HMEMO | 8A / 25A | Reference (order/invoice) + free-text memo. |
| Field | Type | Meaning |
|---|---|---|
| GLSEQ / GLBATCH | DECIMAL(8,0) / (6,0) | Sequence (PK) / batch (YYYYMM). |
| ACCT / DRCR | CHAR(9) / CHAR(1) | Account (1200-AR, 4100-LNDY) / D debit or C credit. |
| AMT / GLREF / GLDT | DECIMAL(11,2) / CHAR(8) / DECIMAL(8,0) | Amount / reference ('INVOICE') / date. |
One-time install: CALL PRESSCO/PCSETUP (creates every file, LF, SQL table and
compiles every program), then CALL PRESSCO/PCREFLD to load the seeded customers, service
price list and routes.
PCORDH header (OSTAT 'N', route/stop or blank) and
one PCORDL row per garment line; add a PCSTOP row for any routed order.SBMJOB CMD(CALL PGM(PRESSCO/PCDAILY)).PCRCPT rows (RCSTAT 'U') before the run so
PCCASHR applies them — retail 'T' against an order, AR 'A' against an invoice.CALL PRESSCO/PCMENU.Pre-checks: confirm the job's library list includes PRESSCO; confirm the reference
data is loaded (a fresh library needs PCREFLD or every price lookup returns 0).
Post-checks after the daily chain:
PCPRICE PRICED= equals the count of new (N) orders; every priced order is now P with
non-zero totals (a 0 total means the price lookup missed — see F.4).PCTAGGEN TAGGED= equals the total non-cancelled lines across priced orders; one tag per line.PCROUTED DELIVERED= + PCCOUNTER COLLECTED= account for every ready order.PCINVGEN INVOICED=/HELD=: held orders are H with a reason (CL/MH/CS); the
AR invariant holds — each invoiced commercial customer's balance rose by exactly the order total.PCHIST with a unique HSEQ.PCROUTW
report shows them D).PCMONTH. Confirm PCAGE bucket counts, PCSTMT LINES=/OPEN=,
PCGLPST ROWS=, and the COBOL PCARRPT reconciliation.SELECT ACCT, DRCR, AMT FROM PRESSCO.PCGLDIST ORDER BY GLSEQ; -- DR 1200-AR total must equal CR 4100-LNDY total (no tax line) -- and equal SUM(INVTOT) over PCINVH less what was posted at prior closes
Reconciling figures (the same ones the cycles simulation checks against a hand-derived oracle):
OPEN= = SUM(AROPEN) over open items.PCARRPT prints RAISED (SUM ARAMT), OPEN (SUM AROPEN),
ITEMS (count) and OVERDUE (ARAGE>0 count); use it as the independent sub-ledger proof.Year-end: submit PCYEAR. Confirm PCARCHIV ARCHIVED= equals the count of
customers with invoiced turnover, and CLOSED= the fully-settled AR items now marked
ARSTAT='C'. Each archive row's HAMT equals that customer's summed invoice history.
Every program is idempotent by construction — each keys off a stable derived history/invoice/tag
key guarded by a CHAIN(EN), or off a persisted status. Re-running any cycle is safe.
| Situation | Behaviour | Action |
|---|---|---|
| Daily chain fails partway | Completed steps left their status/rows; the guards make each a no-op on retry. | Re-submit PCDAILY: priced orders re-price to the same numbers, existing tags are refused, delivered orders are skipped, and the raised invoice is not re-raised. Fully re-runnable. |
| Re-run PCPRICE | Recomputes from LQTY every time. | Stable no-op — totals land identically, never compounded. |
| Order stuck at P after one run | Plant flow advances one stage per run. | Expected — run the daily chain again the next day; two runs take a new order to READY/DELIVERED. |
| Order held at billing (H) | Credit limit / manual hold / inactive customer. | Clear the cause (raise CRLIMIT, set CHOLD='N', set CSTAT='A', or apply cash to free credit); the next PCINVGEN bills it automatically. |
| Re-run PCINVGEN | Invoice number derived from order; guard refuses a duplicate. | No double-billing; balance does not double; held orders are re-tried each run. |
| Re-run PCGLPST | Posts only movement since last close. | Second run posts 0 rows — the DR to 1200-AR nets off what is already there. |
| Price lookup returns 0 unexpectedly | A single-field CHAIN on the 2-field PCSVCP key can silently miss (PG-PRESSCO-001). | PCPRICE already uses a full KLIST (SVCKEY); if a bespoke program mis-prices, check it supplies BOTH GARMTYPE and SVCTYPE. See F.4. |
PCHIST row (with amount and reference) and the AR
sub-ledger (PCAROP) and GL (PCGLDIST) are independently reconciled by
PCARRPT / a GL query, any cycle's effect is fully reconstructable after the fact for
reconciliation and recovery.The complete program surface, from src/sources.mjs. All objects are in library
PRESSCO. The RPG uses column-exact fixed-form C/D specs mixed with /free blocks,
the same convention as the platform's own samples.
PCCUST, 8 PCSVCP and 2
PCROUTE rows. DSPLYs PCREFLD CUST=5 SVC=8 ROUTE=2.N order, walks its lines with READE, looks the
garment×service up in PCSVCP (full KLIST), applies the rush surcharge, updates each line
(LUNITP/LNET/LSTAT='P'), and re-CHAINs the header to stamp
ONETVAL/ORUSHVAL/OTOTVAL, PROMDT and
OSTAT='P'. Idempotent (recomputes from LQTY).P order, issues one PCTAG per line (TSTAT 'I'), tag number derived
'T' + (order-digits×1000 + line). The 9A field forces one prefix char + 8 digits (a
'TG'+8-digit scheme silently truncated — caught in testing). CHAIN(EN) guards both the
tag and its history row.P order whose tags are all R/O to OSTAT='R'.R: order→D, every tag→O, stop→D,
history 'D'. Skips a done stop or a non-R order.R orders with blank OROUTE (walk-in pickup).D or H commercial order: CHAINs the customer, decides the hold
reason (CS/MH/CL), and either holds (OSTAT='H'+reason, no money) or writes PCINVH +
PCAROP + history, raises the customer balance by the total, and sets OSTAT='B'. Retail
orders are skipped. Re-reads OSTAT 'D' OR 'H' so a hold is retried.U receipt: subroutine CSHT (retail 'T' — order D→B, balance
untouched) or CSHA (AR 'A' — reduce the open item and the balance by exactly the applied amount,
apply only what is owed on an over-payment, set INVSTAT/RCSTAT). History 'C'.PCSTOP to the PCROUTP PRTF, page overflow handled.ARDUEDT vs run date 20260930, stamping
ARAGE 0/1/2/3.PCARDLF (oldest-due-first) joined to PCCUST, printed
on PCAGEP, with an open-items total.1200-AR / D via embedded
SQL, and inserts a balanced DR 1200-AR / CR 4100-LNDY pair for the movement.
A refused insert is not counted as a posted row.PCAROP reconciling RAISED / OPEN / ITEMS / OVERDUE.Y archive history row per customer from its I invoice
history (cash rows excluded). Pass 2 closes fully-settled AR items (ARSTAT='C').The per-line price is volume-agnostic — the unit charge does not vary with quantity:
wunit = PRICE base list price for garment x service if LRUSH = 'Y': wunit = PRICE + (PRICE * RUSHPCT / 100) percent uplift LNET = LQTY * wunit order total OTOTVAL = SUM(LNET) over priced lines ORUSHVAL = SUM(rush portion) = SUM(LNET - LQTY*PRICE) PROMDT = ORDDT + max(TURNDAYS) over the order's lines
Worked (SUIT2/DRYCL, price 12.00, rush 50%, qty 2): unit = 12.00 + 6.00 = 18.00; net = 36.00. A
non-rush SHIRT/WASH qty 4: unit 3.50, net 14.00. Order total 50.00, no separate tax line
(INVNET = INVTOT).
Every posting program derives its PCHIST.HSEQ from a stable business key in a
disjoint numeric range, then guards the write with CHAIN(EN) — a re-run
regenerates the same key and is refused, so the durable audit trail never duplicates and ranges never
collide across programs:
| Program | HTYPE | HSEQ range | Derivation |
|---|---|---|---|
| PCTAGGEN | T | 10000000 + | order-digits×100 + line. |
| PCFLOW | F | 20000000 + | tag-digits×10 + stage. |
| PCROUTED / PCCOUNTER | D | 30000000 + | order-digits. |
| PCINVGEN | I | 40000000 + | order-digits. |
| PCCASHR | C | 50000000 + | receipt-digits. |
| PCARCHIV | Y | 60000000 + | customer-digits. |
The commercial invoice number is likewise derived ('IV0' + last-5 of ORDNO) and CHAIN(EN)-
guarded, so re-billing is refused. Every program that CHAINs a file after a cursor-moving read on another
file re-CHAINs to reposition (the source comments each one), the standard discipline on this platform.
Honest platform note. Building PRESSCO/i surfaced one genuine engine bug, logged in
laundry-app/FINDINGS.md and worked around in the app (engine code was not changed for
this task):
PG-PRESSCO-001 — a partial-key CHAIN against a UNIQUE multi-field access path does
not clear the unsupplied trailing key bytes. In ibmi/rpg.js, emitKeyBuild(f, fo,
keyExpr, fill) only clears the trailing key-buffer bytes when the caller passes a non-null
fill. Every partial-key-capable caller (emitSetll, emitReadEq,
emitChain's DUP-key branch, the delete-first path) passes 'LOW-VALUES' —
except emitChain's UNIQUE-key branch, which calls emitKeyBuild with no
fill. The subsequent READ ... KEY IS then compares the full key length including
stale trailing bytes from the previous keyed operation, so a CHAIN supplying only the leading key field of
a multi-field UNIQUE key reports "not found" even when the record exists.
Impact here. PCSVCP is UNIQUE-keyed on (GARMTYPE, SVCTYPE). A bare
WGARM CHAIN(E) PCSVCPR returned not-found for a genuinely-present SHIRT/WASH row, leaving
LUNITP/LNET at 0 and the promise date un-uplifted — silently: no
compile or runtime error, and PCPRICE still reported PRICED=n because its logic
treats not-found as "list price 0". The corruption would flow through pricing, tagging and the invoice total.
Workaround applied. PCPRICE CHAINs PCSVCPR with a KLIST naming both
key fields (SVCKEY → WGARM, WSVC) instead of a single-field
CHAIN — a full key never depends on fill clearing anything, and is independently the more
correct RPG for a 2-field lookup. Any bespoke program in this app must supply the full key on every CHAIN
against a multi-field UNIQUE access path. No other platform bugs were encountered.
PRESSCO/i signals nothing through SQLSTATEs (it is RPG/DDS, not SQL PL); the codes an operator reads are the status letters in the data and the DSPLY summaries:
| Field / code | Values | Meaning |
|---|---|---|
| PCORDH.OSTAT | N P R D H B X | Order lifecycle (A.2). |
| PCORDH.OHOLDR | CL / MH / CS | Credit-limit / manual-hold / customer-not-active hold reason. |
| PCORDL.LSTAT | O P X | Line open / priced / cancelled. |
| PCTAG.TSTAT | I C R O | Intake / cleaning / ready / out. |
| PCSTOP.RSTOPST | P D | Stop pending / done. |
| PCINVH.INVSTAT / PCAROP.ARSTAT | O P (C) | Open / paid (AR also 'C' closed at year-end). |
| PCRCPT.RCSTAT | U A P | Unapplied / fully applied / part-applied. |
| PCCUST.CTYPE / CSTAT / CHOLD | C R / A / Y | Commercial-retail / active / manual hold. |
PCFLOW run.OROUTE collected at the shop by PCCOUNTER, rather than
delivered on a route.RUSHPCT) on the base unit price when a line is flagged
LRUSH='Y'.SBMJOB CMD(CALL PGM(PRESSCO/PCDAILY)).PCORDIQ's
garment-line list is a subfile.