PRESSCO/i — Dry-Cleaning & Commercial Laundry

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

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.

Contents

A. Overview & Architecture ↑ top

A.1 What it does

PRESSCO/i services the full order-to-cash life of a dry-cleaning / commercial-laundry job:

A.2 The order lifecycle (OSTAT)

One field — PCORDH.OSTAT — drives the whole order through the plant and billing. Each daily program advances the orders it is responsible for:

OSTATMeaningSet by
NNew — captured, not yet priced/tagged.Intake (operator writes the header/lines).
PPriced & garments tagged, in the cleaning flow.PCPRICE (then PCTAGGEN tags).
RReady — every garment tag reached READY.PCFLOW (lifts the header once all tags are R).
DDelivered / collected.PCROUTED (routed) or PCCOUNTER (counter).
HDelivered but held at billing — credit limit, manual hold, or inactive customer.PCINVGEN (retried on later runs).
BBilled — commercial invoice raised, or retail receipt taken.PCINVGEN (commercial) / PCCASHR (retail).
XCancelled.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.

A.3 Component & flow

  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 RPCROUTED/PCCOUNTER deliver it (D, tags O) → PCINVGEN bills or holds it, or PCCASHR takes a retail receipt (B). Every posting stamps a PCHIST row.

A.4 Object inventory

ObjectTypeRole
PCCUSTPFCustomer master (commercial 'C' / retail 'R').
PCSVCPPFService price list, keyed garment×service.
PCORDHPFOrder header (OSTAT lifecycle).
PCORDLPFOrder lines — one per garment batch (composite key).
PCTAGPFGarment tags (TSTAT cleaning lifecycle).
PCTAGSTLLFTags keyed by status then tag (plant worklist).
PCROUTEPFDelivery route master.
PCSTOPPFRoute stops (route, stop → order).
PCINVHPFCommercial invoice header.
PCAROPPFAR open items (commercial only, composite key).
PCARDLFLFAR open items keyed by due date (oldest first).
PCRCPTPFReceipts — retail 'T' and AR-cash 'A'.
PCHISTPFDurable audit trail (unique HSEQ, disjoint ranges).
PCGLDISTSQL tableRevenue/AR GL distribution (DR/CR).
PCCUSTD / PCORDD / PCMENUDDSPFCustomer inquiry / order subfile / menu.
PCAGEP / PCROUTPPRTFAR ageing statement / route-stop report.
PCREFLDRPGLESeed reference data (customers, prices, routes).
PCPRICE PCTAGGEN PCFLOW PCROUTED PCCOUNTER PCINVGEN PCCASHRRPGLEThe daily order-to-cash chain.
PCROUTW PCAGE PCSTMT PCARCHIVRPGLEWeekly / monthly / annual batch.
PCGLPSTSQLRPGLEMonthly revenue GL post (embedded SQL).
PCARRPTCBLLEILE COBOL AR control report.
PCCUSTIQ PCORDIQ PCMENURPGLEOnline inquiry programs.
PCSETUP PCDAILY PCWEEK PCMONTH PCYEARCLPBuild + 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.

B. Online Transactions & Screens ↑ top

B.1 The command/entry line

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 thisType on the command line
Open the main menu (routes to both inquiries)CALL PRESSCO/PCMENU
Customer inquiry directlyCALL PRESSCO/PCCUSTIQ
Order/garment inquiry directlyCALL PRESSCO/PCORDIQ
Build every object (once, at install)CALL PRESSCO/PCSETUP
Load the seeded reference dataCALL PRESSCO/PCREFLD
Run the daily order-to-cash cycleCALL PRESSCO/PCDAILY (or SBMJOB it)
Run the weekly / monthly / annual cycleCALL 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.

B.2 The menu & customer inquiry (PCMENU / PCCUSTIQ)

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.

Customer Inquiry - PRESSCO/i Customer number: K00001 Name . . . . . : REGENCY HOTEL GROUP Type . . . . . : C Credit limit . : 8,000.00 Balance . . . : 50.00 Available . . : 7,950.00 On hold . . . : N Customer found. F3=Exit Enter=Inquire

PCCUSTD fields

FieldType (DDS)Shows
ICUST6A input/outputCustomer number keyed in.
DNAME30A outputCustomer name (CNAME).
DTYPE1A outputC commercial / R retail.
DLIMIT / DBAL14A outputCredit limit / current balance (edited from packed 11P2).
DAVAIL14A outputDerived: CRLIMIT - BALANCE.
DHOLD1A outputManual-hold flag (CHOLD).
DMSG50A output"Customer found." / "Customer not found."

B.3 The order/garment subfile (PCORDIQ / PCORDD)

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).

Order Inquiry - PRESSCO/i Order number: ORD00001 Customer . . . : K00001 Status . . . . : B Hold: Route/Stop . . : RT01/1 Order total . : 50.00 Seq Garm Svc Qty Net S 1 SHIRT WASH 4 14.00 P 2 SUIT2 DRYCL 2 36.00 P Order found. F3=Exit Roll=Page Enter=Inquire

PCORDD subfile columns (OSFL)

FieldType (DDS)Shows
SSEQ2Y,0 outputLine sequence (LSEQ).
SGARM / SSVC5A outputGarment type / service type.
SQTY7A outputLine quantity (edited).
SNET12A outputExtended net (LNET, edited).
SSTAT1A outputLine status: O open, P priced/tagged, X cancelled.

Header fields & keys

B.4 Controls & audit workflow (no four-eyes maker–checker)

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:

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.

C. Batch Jobs & the Periodic Cycles ↑ top

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)).

C.1 Full batch program set

ProgramCyclePurposeReadsWrites / effectDSPLY summary
PCPRICEdailyPrice every N order's lines, stamp header totals + promise date.PCORDH, PCORDL, PCSVCPPCORDL (LUNITP/LNET/LSTAT=P), PCORDH (totals, OSTAT=P).PCPRICE PRICED=n SKIP=n
PCTAGGENdailyIssue one garment tag per line of every P order.PCORDH, PCORDLPCTAG (TSTAT=I), PCHIST (T).PCTAGGEN TAGGED=n SKIP=n
PCFLOWdailyAdvance every tag one stage (I→C, C→R); lift a fully-ready order to R.PCTAG, PCORDHPCTAG (advanced), PCORDH (OSTAT=R), PCHIST (F).PCFLOW ADVANCED=n
PCROUTEDdailyDeliver every R order on a pending route stop.PCSTOP, PCORDH, PCTAGPCORDH (D), PCTAG (O), PCSTOP (RSTOPST=D), PCHIST (D).PCROUTED DELIVERED=n SKIP=n
PCCOUNTERdailyCollect every R order with no route (counter pickup).PCORDH, PCTAGPCORDH (D), PCTAG (O), PCHIST (D).PCCOUNTER COLLECTED=n SKIP=n
PCINVGENdailyInvoice every delivered commercial order, or hold it on credit control.PCORDH, PCCUSTPCINVH, PCAROP, PCHIST (I), PCCUST (balance +total), PCORDH (B or H).PCINVGEN INVOICED=n HELD=n SKIP=n
PCCASHRdailySettle unapplied receipts: retail 'T' (order→B) and AR 'A' (reduce balance/open item).PCRCPT, PCORDH, PCAROP, PCCUST, PCINVHPCRCPT (RCAPPL/RCSTAT), balances, PCHIST (C).PCCASHR APPLIED=n SKIP=n
PCROUTWweeklyPrint every route stop and its status (PRTF).PCSTOPPCROUTP report.PCROUTW STOPS=n
PCAGEmonthlyStamp 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
PCSTMTmonthlyPrint the AR ageing statement, oldest-due-first (PRTF).PCAROP (via PCARDLF), PCCUSTPCAGEP report.PCSTMT LINES=n OPEN=n
PCGLPSTmonthlyPost the balanced revenue GL movement (DR AR / CR revenue).PCINVH, PCGLDISTPCGLDIST (DR/CR pair).PCGLPST BATCH=n ROWS=n
PCARRPTmonthlyCOBOL AR control report reconciling the open-item ledger.PCAROPDSPLY totals.PCARRPT RAISED/OPEN/ITEMS/OVERDUE
PCARCHIVannualWrite a per-customer turnover archive row; close settled AR items.PCCUST, PCHIST, PCAROPPCHIST (Y), PCAROP (ARSTAT=C).PCARCHIV ARCHIVED=n CLOSED=n SKIP=n

C.2 The daily order-to-cash chain (PCDAILY)

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
The invoice number is derived from the order number ('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.

C.3 Weekly / monthly / annual

Weekly — PCWEEK

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.

Monthly — PCMONTH

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.

Annual — PCYEAR

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').

C.4 Ordering & dependencies

D. Data Files (data dictionary) ↑ top

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.

PCCUST — Customer master (UNIQUE key CUSTNO)

FieldTypeMeaning
CUSTNO6ACustomer number (key). K* commercial, R* retail by convention.
CNAME / CADDR / CPHONE30A / 30A / 12AName, address, phone.
CTYPE1AC commercial (billed on statement, carries AR), R retail (pay-at-pickup, no AR ever).
CRLIMIT11P 2Credit limit — meaningful only for CTYPE 'C'.
BALANCE11P 2Outstanding AR balance — only ever moved for a commercial account.
CTERMS3S 0Payment terms in days (AR due date = invoice date + terms).
CHOLD1AManual hold: 'Y' blocks billing (reason 'MH').
CSTAT1AAccount status: 'A' active (billing needs this; otherwise 'CS').
CREVYR4S 0Revenue 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).

PCSVCP — Service price list (UNIQUE key GARMTYPE, SVCTYPE)

FieldTypeMeaning
GARMTYPE5AGarment type (key 1): SHIRT, TROUS, SUIT2, DRESS, COAT, DUVET, CURTN, LINEN.
SVCTYPE5AService type (key 2): WASH, DRYCL, PRESS, ALTER.
PRICE9P 2Per-garment base charge.
RUSHPCT5P 2Rush surcharge percent applied when a line is flagged rush.
TURNDAYS2S 0Standard turnaround; the slowest line's turnaround sets the promise date.
PSTAT1APrice-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).

PCORDH — Order header (UNIQUE key ORDNO)

FieldTypeMeaning
ORDNO8AOrder number (key), e.g. ORD00001.
CUSTNO6AOwning customer.
ORDDT / PROMDT8S 0Intake date / promise date (= intake + slowest turnaround).
OSTAT1ALifecycle: N/P/R/D/H/B/X (see A.2).
OHOLDR2AHold reason when held at billing: 'CL' / 'MH' / 'CS'.
ONETVAL / ORUSHVAL / OTOTVAL11P 2Net value / rush portion / order total, stamped by pricing.
OINVNO8AInvoice number once billed (commercial).
OROUTE / OSTOPSQ4A / 3S 0Delivery route / stop; blank OROUTE = counter pickup.

PCORDL — Order lines (UNIQUE composite key ORDNO, LSEQ)

FieldTypeMeaning
ORDNO / LSEQ8A / 3S 0Order + line sequence (composite key).
GARMTYPE / SVCTYPE5A / 5AGarment + service (looked up in PCSVCP).
LQTY5S 0Garments on this line.
LRUSH1A'Y' rush requested — surcharge applies.
LUNITP / LNET9P 2 / 11P 2Priced unit charge (after rush) / extended net = LQTY×LUNITP.
LSTAT1A'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.

PCTAG — Garment tags (UNIQUE key TAGNO) + PCTAGSTL (LF by TSTAT, TAGNO)

FieldTypeMeaning
TAGNO9AGlobally-unique tag number (key), derived 'T' + (order-digits×1000 + line).
ORDNO / LSEQ8A / 3S 0Order + line the tag belongs to.
GARMTYPE5AGarment type.
TSTAT1A'I' intake, 'C' in cleaning, 'R' ready, 'O' out.
TDT8S 0Tag 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.

PCROUTE — Route master / PCSTOP — Route stops

File / FieldTypeMeaning
PCROUTE.RTNO / RTNAME / RSTAT4A / 20A / 1ARoute number (key), name, status. Seeded RT01 CITY CENTRE LOOP, RT02 WEST SIDE HOTELS.
PCSTOP (key RTNO, STOPSQ)One row per (route, stop).
PCSTOP.ORDNO / CUSTNO8A / 6AOrder this stop delivers, and its customer.
PCSTOP.RSTOPST1A'P' pending, 'D' done.

PCINVH — Invoice header (UNIQUE key INVNO)

FieldTypeMeaning
INVNO8AInvoice number (key), derived from the order number.
ORDNO / CUSTNO8A / 6ABilled order and customer.
INVDT8S 0Invoice date.
INVNET / INVTOT11P 2Net / total (equal in this design — no tax line).
INVSTAT1A'O' open, 'P' paid.

PCAROP — AR open items (UNIQUE composite key CUSTNO, INVNO) + PCARDLF (LF by ARDUEDT, CUSTNO)

FieldTypeMeaning
CUSTNO / INVNO6A / 8ACustomer + invoice (composite key). Commercial only.
ARINVDT / ARDUEDT8S 0Invoice date / due date (= invoice date + terms).
ARAMT / AROPEN11P 2Invoiced amount / outstanding (INVTOT less cash applied).
ARAGE1S 0Ageing bucket 0/1/2/3, stamped by PCAGE.
ARSTAT1A'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.

PCRCPT — Receipts (UNIQUE key RCPTNO)

FieldTypeMeaning
RCPTNO / CUSTNO8A / 6AReceipt number (key) + customer.
RCTYPE1A'T' retail counter (settles an order, no AR), 'A' AR application (settles an invoice open item).
RCORDNO / RCINVNO8A / 8AOrder settled (T) / invoice settled (A).
RCDT / RCAMT / RCAPPL8S 0 / 11P 2Date / tendered / actually applied (over-payment applies only what is owed).
RCSTAT1A'U' unapplied, 'A' fully applied, 'P' part-applied.

PCHIST — Durable audit trail (UNIQUE key HSEQ)

FieldTypeMeaning
HSEQ8S 0History key (key) — derived, disjoint range per program (see F.3).
CUSTNO / HITEM6A / 9ACustomer + item (tag no / 'INVOICE' / 'ROUTE' etc.).
HTYPE1AT tag, F flow, D delivery, I invoice, C cash, Y archive.
HDT / HQTY / HAMT8S 0 / 5S 0 / 11P 2Date / quantity / amount (signed; cash rows negative).
HREF / HMEMO8A / 25AReference (order/invoice) + free-text memo.

PCGLDIST — GL distribution (SQL table, PK GLSEQ; index PCGLDACC on ACCT, DRCR)

FieldTypeMeaning
GLSEQ / GLBATCHDECIMAL(8,0) / (6,0)Sequence (PK) / batch (YYYYMM).
ACCT / DRCRCHAR(9) / CHAR(1)Account (1200-AR, 4100-LNDY) / D debit or C credit.
AMT / GLREF / GLDTDECIMAL(11,2) / CHAR(8) / DECIMAL(8,0)Amount / reference ('INVOICE') / date.

Relationships

E. Operations Runbook ↑ top

E.1 Day-in-the-life

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.

  1. Capture the day's orders: write the PCORDH header (OSTAT 'N', route/stop or blank) and one PCORDL row per garment line; add a PCSTOP row for any routed order.
  2. Submit the daily chain: SBMJOB CMD(CALL PGM(PRESSCO/PCDAILY)).
  3. For any settled receipts, write PCRCPT rows (RCSTAT 'U') before the run so PCCASHR applies them — retail 'T' against an order, AR 'A' against an invoice.
  4. Post-check the run (below). Remember an order needs two daily runs to reach billed, because the plant flow advances one stage per run.
  5. Review orders/customers online with 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:

E.2 Month-end & year-end close

  1. Confirm the month's daily cycles have run and every routed stop is delivered (weekly PCROUTW report shows them D).
  2. Submit PCMONTH. Confirm PCAGE bucket counts, PCSTMT LINES=/OPEN=, PCGLPST ROWS=, and the COBOL PCARRPT reconciliation.
  3. Reconcile the GL against the invoice register:
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):

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.

E.3 Failure & re-run rules

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.

SituationBehaviourAction
Daily chain fails partwayCompleted 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 PCPRICERecomputes from LQTY every time.Stable no-op — totals land identically, never compounded.
Order stuck at P after one runPlant 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 PCINVGENInvoice number derived from order; guard refuses a duplicate.No double-billing; balance does not double; held orders are re-tried each run.
Re-run PCGLPSTPosts 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 unexpectedlyA 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.
Because every stage stamps a 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.

F. Developer Reference ↑ top

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.

F.1 Program surface

PCREFLD (RPGLE)
Seeds the reference data by writing 5 PCCUST, 8 PCSVCP and 2 PCROUTE rows. DSPLYs PCREFLD CUST=5 SVC=8 ROUTE=2.
PCPRICE (RPGLE)
Reads every order; for an 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).
PCTAGGEN (RPGLE)
For each 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.
PCFLOW (RPGLE)
Pass 1 advances every tag one stage from its persisted TSTAT (I→C, C→R) and stamps flow history. Pass 2 lifts any P order whose tags are all R/O to OSTAT='R'.
PCROUTED (RPGLE)
For each pending stop whose order is R: order→D, every tag→O, stop→D, history 'D'. Skips a done stop or a non-R order.
PCCOUNTER (RPGLE)
Same as PCROUTED but for R orders with blank OROUTE (walk-in pickup).
PCINVGEN (RPGLE)
For each 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.
PCCASHR (RPGLE)
For each 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'.
PCROUTW (RPGLE)
Weekly route-stop report over PCSTOP to the PCROUTP PRTF, page overflow handled.
PCAGE (RPGLE)
Monthly ageing: 30/360 serial arithmetic on ARDUEDT vs run date 20260930, stamping ARAGE 0/1/2/3.
PCSTMT (RPGLE)
Monthly statement over PCARDLF (oldest-due-first) joined to PCCUST, printed on PCAGEP, with an open-items total.
PCGLPST (SQLRPGLE)
Sums the invoice register, nets off what is already posted to 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.
PCARRPT (ILE COBOL)
Sequential read of PCAROP reconciling RAISED / OPEN / ITEMS / OVERDUE.
PCARCHIV (RPGLE)
Pass 1 writes one Y archive history row per customer from its I invoice history (cash rows excluded). Pass 2 closes fully-settled AR items (ARSTAT='C').
PCCUSTIQ / PCORDIQ / PCMENU (RPGLE)
The two read-only inquiry screens and the menu driver (B.2/B.3).

F.2 Pricing & the rush surcharge

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).

F.3 Idempotency & the history key ranges

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:

ProgramHTYPEHSEQ rangeDerivation
PCTAGGENT10000000 +order-digits×100 + line.
PCFLOWF20000000 +tag-digits×10 + stage.
PCROUTED / PCCOUNTERD30000000 +order-digits.
PCINVGENI40000000 +order-digits.
PCCASHRC50000000 +receipt-digits.
PCARCHIVY60000000 +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.

F.4 Platform finding — PG-PRESSCO-001

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 (SVCKEYWGARM, 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.

F.5 Status-code reference

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 / codeValuesMeaning
PCORDH.OSTATN P R D H B XOrder lifecycle (A.2).
PCORDH.OHOLDRCL / MH / CSCredit-limit / manual-hold / customer-not-active hold reason.
PCORDL.LSTATO P XLine open / priced / cancelled.
PCTAG.TSTATI C R OIntake / cleaning / ready / out.
PCSTOP.RSTOPSTP DStop pending / done.
PCINVH.INVSTAT / PCAROP.ARSTATO P (C)Open / paid (AR also 'C' closed at year-end).
PCRCPT.RCSTATU A PUnapplied / fully applied / part-applied.
PCCUST.CTYPE / CSTAT / CHOLDC R / A / YCommercial-retail / active / manual hold.

G. Glossary ↑ top

AR open item (PCAROP)
An outstanding commercial invoice on the receivables ledger, carrying its due date, open amount and ageing bucket. Retail orders never post here.
AR invariant
The rule that a commercial invoice raises the customer balance by exactly its total, a cash application reduces it by exactly the amount applied (never more than owed), and a retail receipt never touches the balance.
Ageing bucket
0 current, 1 (1–30 days), 2 (31–60), 3 (61+) — how overdue an open item is, on the 30/360 day convention.
Cleaning-plant flow (TSTAT)
The garment-tag lifecycle intake→cleaning→ready→out, advanced one stage per daily PCFLOW run.
Counter pickup
An order with blank OROUTE collected at the shop by PCCOUNTER, rather than delivered on a route.
Credit control (at billing)
Refusing to invoice a commercial order that breaches the credit limit or belongs to a held/inactive account; the order is held (OSTAT='H') and retried later. Bites at billing, not intake, because the service is already rendered.
Idempotent
Safe to run again with the same result. Every PRESSCO/i program is, via derived, CHAIN(EN)-guarded keys and status gating.
Promise date (PROMDT)
Intake date plus the slowest garment's turnaround on the order — when the customer is told the order will be ready.
Rush surcharge
A percent uplift (RUSHPCT) on the base unit price when a line is flagged LRUSH='Y'.
SBMJOB
Submit Job — the IBM i command that queues a program to run as batch, e.g. SBMJOB CMD(CALL PGM(PRESSCO/PCDAILY)).
Subfile
A 5250 display construct listing many rows on one screen (DDS SFL/SFLCTL). PCORDIQ's garment-line list is a subfile.
Tag (garment tag, PCTAG)
The physical tracking unit issued per order line, carrying the plant status; its number is printed on the garment and derived from the order and line.
Turnaround (TURNDAYS)
Standard days for a garment×service; the slowest one on an order sets the promise date.