CRAFTORD/i — Craft-Supplies Wholesale Purchasing

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

CRAFTORD/i is a craft-supplies wholesale purchase-order & receiving application: a supplier and item master, purchase-order headers and lines, an interactive 5250 subfile screen for keying PO lines, a heaviest-priced-item report, and a purchase-order detail report. The application is built almost entirely from DDS — physical files, logical files (including a join logical file), a display file with a subfile, and a printer file — and it is honestly a DDS reference / showcase application: it was authored to exercise the platform's DDS support (PF float and VARLEN fields, validity keywords and reference inheritance, LF RENAME / select-omit / join / descending access paths, DSPF subfile / OR-conditioning / DATE-TIME-USER specials / edit-code modifiers / ALARM, and PRTF edit words) through real compiled RPG programs rather than by driving the DDS parsers directly. It nonetheless behaves as a coherent little purchasing system, and this manual documents it as one, for the operator who runs the screen and the reports and for the developer maintaining the DDS. It is grounded entirely in the committed source (dds-app/src/sources.mjs, src/seed.mjs, and the test/dds_app_battle.mjs driver).

Contents

A. Overview & Architecture ↑ top

A.1 What it does

CRAFTORD/i manages the purchasing side of a craft-supplies wholesaler:

A.2 A DDS-first application (an honest statement)

CRAFTORD/i is deliberately DDS-first: the interesting content lives in the eight DDS members, and the three RPG programs are thin drivers that exist to prove those DDS objects compile and behave correctly when a real program uses them. The source header says so plainly — the application is a QA vehicle for the platform's DDS support, and “every one of those features is exercised by a REAL compiled program against REAL DDS source… not by calling ibmi/dds.js directly.” Read this manual as the operations reference for a small but complete purchasing app whose data definitions are the star; Section D is the fullest section, and Section F maps each DDS feature to the object that demonstrates it.

Everything runs in library CRAFTORD. Source is held in three source physical files: QDDSSRC (the eight DDS members), QRPGLESRC (the three RPG programs), and QCLSRC (the build script CRBUILD).

A.3 Component & flow

  MASTER DATA            ONLINE                    BATCH (reports)
  -----------            ------                    ---------------
  CRSUPP  (suppliers)    CRPOENT (5250)            CRWTRPT  --> CRITMDL (LF)
  CRITEM  (items)          subfile PSFL/PCTL         DESCEND over UNITPR
    |  REFFLD SUPID         over CRPOD                heaviest-priced first
    v                       writes CRPOL + CRPO     CRPORPT  --> CRPOJL (join LF)
  CRPO   (PO header) <---write---  CRPOENT            CRPO x CRPOL, AND sel/omit
    |                                                 --> CRPOP (PRTF report)
    +--> CRPOL (PO line)  <---write--- CRPOENT

  CRBUILD (CL) --CRTPF/CRTLF/CRTDSPF/CRTPRTF/CRTBNDRPG--> every object above

A single PO-entry event flows: the operator runs CRPOENT → keys the header (PONO, HSUPID, HREF) and a first line → the DSPF-level validation on HSUPID (inherited VALUES from CRSUPP.SUPID) runs before the program body → each accepted line is written to the PSFL subfile and to CRPOL → on exit the header row is written to CRPO. Reporting is a separate, read-only path: CRWTRPT and CRPORPT read the logical files and produce DSPLY / spooled output.

A.4 Object inventory

ObjectTypeRole
CRSUPPPFSupplier master (source of the validity keywords others inherit).
CRITEMPFItem master (float WEIGHT, VARLEN NOTES, REFFLD SUPID).
CRPOPFPurchase-order header.
CRPOLPFPurchase-order line.
CRITMDLLFSimple LF over CRITEM: RENAME + DESCEND access path.
CRPOJLJoin LFCRPOL ⨯ CRPO join, DESCEND key, AND select/omit.
CRPODDSPFSubfile PO-entry screen (SFL/SFLCTL).
CRPOPPRTFPO detail report (edit words, OR conditions, overflow).
CRWTRPTRPGLEHeaviest-priced-item report (drives CRITMDL).
CRPORPTRPGLEPO detail report (drives CRPOJL + CRPOP).
CRPOENTRPGLEInteractive PO-entry program (drives CRPOD).
CRBUILDCLLEBuild script: creates every object above.

The full catalogue is 4 PFs, 2 LFs, 1 DSPF and 1 PRTF, driven by 3 RPG programs and 1 CL build script. Section D expands each file; Section F maps the DDS features they demonstrate.

B. Online Transactions & Screens ↑ top

B.1 The command/entry line

CRAFTORD/i has no CICS transaction identifiers and no menu. On IBM i each program is reached by name from a 5250 command-entry line (or a JOBQ/scheduler for the batch reports). 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 CRAFTORD — the tested session runs with LIBL = QSYS QGPL CRAFTORD QTEMP and CURLIB = CRAFTORD.

To do thisType on the command line
Open the interactive PO-entry screenCALL CRAFTORD/CRPOENT
Run the heaviest-priced-item reportCALL CRAFTORD/CRWTRPT (or SBMJOB it)
Run the PO detail report (spooled)CALL CRAFTORD/CRPORPT (or SBMJOB it)
(Re)build every object from sourceCALL CRAFTORD/CRBUILD

None of the programs takes CALL parameters. Only CRPOENT is interactive; the two report programs run to completion, DSPLY a one-line summary, and (for CRPORPT) leave a spooled file.

B.2 The PO-entry subfile screen (CRPOENT / CRPOD)

CRPOENT is the one interactive program. The CRPOD display file pairs a subfile (record PSFL, SFL) with its control record (PCTL, SFLCTL(PSFL), SFLSIZ(0010), SFLPAG(0005), ROLLUP(25)/ROLLDOWN(26)). The header and the line-entry fields live on the always-present PCTL control record; each accepted line is WRITEn to the PSFL subfile so the growing order is visible as the operator keys it.

CRAFTORD/i Purchase Order Entry 08/07/26 14:22:07 QPGMR NEW PO PO____ S0002 ________AB Add line: Item Qty Price I00001 00003 0012.50 Seq Item Qty Price 1 I00001 00003 $12.50 Line and header captured. F3=Exit Roll=Page Enter=Add line

Header & line-entry fields (PCTL control record)

FieldType (DDS)Role
PONO6A input/outputPurchase-order number keyed by the operator (required).
HSUPIDREF (5A) input/outputSupplier code; REFFLD(SUPID CRSUPP) so it inherits the VALUES('S0001' 'S0002' 'S0003' 'S0004') validity list (required).
HREF10A input/output, CHECK(RB)Free-text PO reference code, stored/rendered right-adjusted.
IITEM6A input/outputItem number for the next line to add.
IQTY5S,0 input/outputQuantity for the next line.
IPRICE7S,2 input/outputLine price for the next line.
DMSG50A output, DSPATR(HI)Highlighted message line (row 22) for validation feedback.

Subfile columns (PSFL record) — output-only

FieldType (DDS)Shows
SLSEQ2S,0 outputLine sequence within the order.
SITEM6A outputItem number of the accepted line.
SQTY5S,0 outputQuantity.
SPRICE7S,2 output, EDTCDE(1 $)Line price with the floating-dollar edit-code modifier (e.g. $12.50).
Why the entry fields are on the control record, not the subfile. The subfile columns SITEM/SQTY/SPRICE are output-only: a subfile record does not exist to type into until the program WRITEs it. So the real entry fields (IITEM/IQTY/IPRICE) live on the always-present PCTL control record, and the program copies them into the subfile fields on the way into each new PSFL row. An earlier draft that made the subfile columns themselves keyable failed for exactly this reason (recorded as own-app finding DDS-QA-06; see Section F.3). Operationally: type the next line into the “Add line” row and press Enter; it appears in the list.

Actions & keys

B.3 Validation, the OR-conditioned banner & ALARM

CRAFTORD/i's PO-entry screen demonstrates three distinct DDS-driven behaviours the operator sees directly:

The DDS rule the OR-condition exercises is that O connects multiple indicator lines of one field, not two independent field specs. An earlier draft OR-connected two separate constant lines and the platform correctly refused to treat them as one OR-group (own-app finding DDS-QA-05; see Section F.3). The banner as shipped is a single OR-conditioned field and flips as described.

The header also carries three DDS specials rendered by the platform, not the program: DATE with EDTCDE(Y) (mm/dd/yy, top-right), TIME (hh:mm:ss), and USER (the job user, e.g. QPGMR). And HREF carries CHECK(RB) (right-adjust, blank-fill): AB keyed left-justified is stored and re-rendered as eight blanks then AB.

C. Batch Jobs & the Reporting Cycle ↑ top

CRAFTORD/i's batch side is a build/seed step plus two read-only reports. There is no posting or period-close cycle — the reports simply drive the two logical files and produce output. All jobs take no CALL parameters; each is a bare CALL (or SBMJOB).

C.1 Build & seed

The application is created from source by the CRBUILD CL program (member QCLSRC/CRBUILD, source constant S.BUILDCL). It is a pure CL build script that creates each object from its source member, in dependency order:

-- CRBUILD, in order:
CRTPF    FILE(CRAFTORD/CRSUPP)  SRCFILE(CRAFTORD/QDDSSRC)   SRCMBR(CRSUPP)
CRTPF    FILE(CRAFTORD/CRITEM)  SRCFILE(CRAFTORD/QDDSSRC)   SRCMBR(CRITEM)
CRTPF    FILE(CRAFTORD/CRPO)    SRCFILE(CRAFTORD/QDDSSRC)   SRCMBR(CRPO)
CRTPF    FILE(CRAFTORD/CRPOL)   SRCFILE(CRAFTORD/QDDSSRC)   SRCMBR(CRPOL)
CRTLF    FILE(CRAFTORD/CRITMDL) SRCFILE(CRAFTORD/QDDSSRC)   SRCMBR(CRITMDL)
CRTLF    FILE(CRAFTORD/CRPOJL)  SRCFILE(CRAFTORD/QDDSSRC)   SRCMBR(CRPOJL)
CRTDSPF  FILE(CRAFTORD/CRPOD)   SRCFILE(CRAFTORD/QDDSSRC)   SRCMBR(CRPOD)
CRTPRTF  FILE(CRAFTORD/CRPOP)   SRCFILE(CRAFTORD/QDDSSRC)   SRCMBR(CRPOP)
CRTBNDRPG PGM(CRAFTORD/CRWTRPT) SRCFILE(CRAFTORD/QRPGLESRC) SRCMBR(CRWTRPT)
CRTBNDRPG PGM(CRAFTORD/CRPORPT) SRCFILE(CRAFTORD/QRPGLESRC) SRCMBR(CRPORPT)
CRTBNDRPG PGM(CRAFTORD/CRPOENT) SRCFILE(CRAFTORD/QRPGLESRC) SRCMBR(CRPOENT)
SNDPGMMSG MSG('CRAFTORD build complete.')

The order matters: the PFs are created first (the LFs and the REF fields depend on them), then the two LFs, the DSPF and PRTF, then the RPG programs (which are compiled against the files). Loading the source members into QDDSSRC/QRPGLESRC/QCLSRC is done by seedCraftord() (src/seed.mjs); seeding master data (suppliers, items, POs) is done by the operator or the test harness after the build — there is no seed RPG program.

C.2 The two reports

ProgramPurposeReadsOutput
CRWTRPT List items most-expensive first. CRITMDL (simple LF, DESCEND over UNITPR, ITEMNO renamed INO). One DSPLY per item (CRWTRPT n INO UNITPR) in descending price order, then CRWTRPT DONE n.
CRPORPT Print PO detail for open, larger-quantity lines. CRPOJL (join LF: CRPOLCRPO, DESCEND on LNPRICE, select POSTAT='O' AND QTY>5). A spooled report through CRPOP (header/column/detail/total records); DSPLY summary CRPORPT LINES=n TOTAL=v.

CRWTRPT — heaviest-priced-item report

CRWTRPT opens CRITMDL keyed, does SETLL *LOVAL then reads forward — because the access path is DESCEND over UNITPR, forward reads come out most-expensive first. It reads the renamed field INO (proving RENAME is live) and DSPLYs each item, counting them.

Expected DSPLY (seeded items 34.75, 21.00, 12.50, 9.00):
  CRWTRPT 1 I00002 34.75      Glass Bead Mix — highest price first
  CRWTRPT 2 I00004 21.00
  CRWTRPT 3 I00001 12.50
  CRWTRPT 4 I00003 9.00
  CRWTRPT DONE 4              all 4 visited, none dropped/duplicated
The LF's source comment is honest about a platform boundary: the item's shipping weight (CRITEM.WEIGHT) is a DDS float field (type F), which a compiled RPG program cannot read (the platform raises SNF5002 on an F-type externally-described field). So although the file is nominally the “heaviest-item” view, its RPG-visible access path is a descending UNITPR (an S-type, RPG-readable field) — heaviest by price. The float weight is still stored and round-trips correctly through the data store (verified in the battle test); it is simply not surfaced to RPG.

CRPORPT — PO detail report

CRPORPT reads the join LF CRPOJL and prints through CRPOP. The join fans a PO header out across its lines; the LF's AND-connected select/omit keeps only lines where POSTAT='O' AND QTY>5, and its DESCEND key on LNPRICE orders the kept rows most-expensive-line first. Because the key is descending, the program reads from the top with *HIVAL SETGT then reads forward — the documented real-IBM-i idiom for a descending file (the source cites the corpus program that reads its own DESCEND join LF the same way). Each row sets a high-value flag indicator (*IN21 when LNPRICE>30) and accumulates a total, and the printer file handles page overflow via OFLIND(*IN90) — when the overflow indicator trips, the program re-writes the header and column records.

Expected (seeded POs — only PO0001's two open, QTY>5 lines qualify):
  spool: I00002 (34.75) prints before I00003 (9.00)   DESCEND on LNPRICE
  money column asterisk-protected:  **034.75  /  **009.00
  DSPLY: CRPORPT LINES= 2 TOTAL=43.75

C.3 Ordering & dependencies

D. Data Files (data dictionary) ↑ top

This is the fullest section: in a DDS-first application the file definitions are the content. All objects are in library CRAFTORD, grounded in the eight DDS members in src/sources.mjs. Field types follow DDS column-position conventions: A character, S zoned decimal (with a decimal-places digit), F floating-point.

D.1 Physical files

CRSUPP — Supplier master (key SUPID)

The source of validity keywords the rest of the application inherits. Every field except the name carries a validity keyword, captured on the PF per the DDS reference-inheritance rule and inherited by any field defined REFFLD/REF against it.

FieldTypeValidity / keywordMeaning
SUPID5A (key)VALUES('S0001' 'S0002' 'S0003' 'S0004')Supplier code — must be one of the four seeded values. This is the field CRITEM, CRPO and the DSPF's HSUPID all reference.
SUPNAME25ASupplier name.
STATE2AVALUES('CA' 'OR' 'WA' 'NV')State — a separate VALUES list.
RATING1S,0RANGE(1 5)Supplier rating 1–5.
TERMS4ACHECK(ME)Payment terms — mandatory entry.

Seeded suppliers: S0001 Pacific Fiber Mill (OR, rating 4, N30) and S0002 Sierra Bead Works (CA, rating 5, N60).

CRITEM — Item master (key ITEMNO)

Carries the two most distinctive PF field types in the app: a floating-point field and a variable-length field, plus a referenced field.

FieldTypeKeywordMeaning
ITEMNO6A (key)Item number, e.g. I00001.
DESC20AItem description.
SUPIDREF (5A)REFFLD(SUPID CRSUPP)Owning supplier — inherits CRSUPP.SUPID's type, length and VALUES list.
UNITPR7S,2Per-unit price (the RPG-visible field CRITMDL keys on).
WEIGHT9F,4FLTPCN(*DOUBLE)Per-unit shipping weight (oz), a DDS float field; *DOUBLE makes it a genuine 8-byte IEEE double. Round-trips through the store; not RPG-readable (SNF5002).
NOTES60AVARLEN(20)Free-text notes stored variable-length (a real 2-byte length prefix; allocated length 60, expected length 20).

Seeded items (distinct UNITPR so the DESCEND order is unambiguous): I00001 Wool Roving 12.50 / I00002 Glass Bead Mix 34.75 / I00003 Bamboo Needles 9.00 / I00004 Silk Ribbon 21.00.

CRPO — Purchase-order header (key PONO)

FieldTypeKeywordMeaning
PONO6A (key)Purchase-order number, e.g. PO0001.
SUPIDREF (5A)REFFLD(SUPID CRSUPP)Supplier (inherits VALUES).
PODATE8S,0PO date, YYYYMMDD.
POSTAT1AVALUES('O' 'C' 'X')Status: O open, C closed, X cancelled. Drives the join LF's select rule.

CRPOL — Purchase-order line (key PONO, LSEQ)

FieldTypeKeywordMeaning
PONO6A (key)Owning PO number.
LSEQ2S,0 (key)Line sequence within the order.
ITEMNOREF (6A)REFFLD(ITEMNO CRITEM)Item ordered (inherits from CRITEM.ITEMNO).
QTY5S,0Quantity. Drives the join LF's QTY>5 select clause.
LNPRICE7S,2Line price. The join LF's DESCEND key.
CRPO.PONO and CRPOL.PONO are the same externally-described RPG field name across two files — per the real IBM i rule, unqualified externally-described fields sharing a name become one global RPG variable. CRPOENT relies on this: PONO is read once from the header input and stays populated across both the POLREC (line) and POHREC (header) writes.

D.2 Logical files

CRITMDL — Simple LF over CRITEM (RENAME + DESCEND)

A single-format logical file (PFILE(CRITEM)) that demonstrates two features at once:

Keyed on UNITPR (not the float WEIGHT) precisely because UNITPR is RPG-readable while a DDS float field is not — see the note in C.2.

CRPOJL — Join logical file (CRPOL ⨯ CRPO)

The richest DDS object in the application: a join logical file with a descending key and an AND-connected select/omit. Source shape:

     A                                       JDFTVAL
     A          R POJREC                     JFILE(CRPOL CRPO)
     A          J                            JOIN(CRPOL CRPO)
     A                                       JFLD(PONO PONO)
     A            PONO / LSEQ / ITEMNO / QTY / LNPRICE   JREF(1)   from CRPOL
     A            SUPID / PODATE / POSTAT                JREF(2)   from CRPO
     A          K LNPRICE                    DESCEND
     A          K PONO
     A          S POSTAT                     COMP(EQ 'O')
     A            QTY                        COMP(GT 5)             ANDed with the S rule
     A          O                            ALL

With the seeded data, only PO0001's two lines (open PO, QTY 10 and 8, both >5) survive the select; DESCEND on LNPRICE puts 34.75 before 9.00. PO0002 (QTY 2 fails >5) and PO0003 (status C fails ='O') are omitted.

D.3 Display & printer files

CRPOD — Display file (subfile PO-entry screen)

Two records: the subfile PSFL (SFL) and its control PCTL (SFLCTL(PSFL)). Field-level detail is in Section B.2; the DDS features it demonstrates:

CRPOP — Printer file (PO detail report)

Four records driving the spooled report from CRPORPT:

RecordKeyword(s)Role
PHDRSKIPB(001) SPACEA(002), HIGHLIGHTReport title banner.
PCOLSPACEA(001)Column headings.
PDTLSPACEB(001)One detail line per join row.
PTOTSPACEB(002)Lines-printed count and total line value.

Notable detail fields on PDTL:

The file is opened with OFLIND(*IN90) so CRPORPT can detect page overflow and re-emit the header/column records.

D.4 Relationships

E. Operations Runbook ↑ top

E.1 Build-from-scratch

  1. Confirm the job's library list includes CRAFTORD and CURLIB=CRAFTORD.
  2. Create the library if needed: CRTLIB LIB(CRAFTORD).
  3. Load the source members (DDS, RPG, CL) into QDDSSRC/QRPGLESRC/QCLSRC — done programmatically by seedCraftord().
  4. Create the build program: CRTCLPGM PGM(CRAFTORD/CRBUILD) SRCFILE(CRAFTORD/QCLSRC) SRCMBR(CRBUILD).
  5. Run it: CALL PGM(CRAFTORD/CRBUILD). Watch for CRAFTORD build complete.
  6. Confirm the objects exist: four PFs (CRSUPP/CRITEM/CRPO/CRPOL), two LFs (CRITMDL/CRPOJL), one DSPF (CRPOD), one PRTF (CRPOP), three programs (CRWTRPT/CRPORPT/CRPOENT).
  7. Seed master data (suppliers, items) and any starting PO data before running the reports.

E.2 Day-in-the-life

  1. Enter POs. CALL CRAFTORD/CRPOENT; key the header (PO number + a valid supplier code from the VALUES list + an optional reference), add lines, then F3 to write the order. Watch for the “NEW PO”→“EXISTING PO” banner flip as confirmation the header was accepted.
  2. Review pricing. CALL CRAFTORD/CRWTRPT for a most-expensive-first item list (verify the DSPLY ends CRWTRPT DONE n with n = item count).
  3. Print PO detail. CALL CRAFTORD/CRPORPT (or SBMJOB it) to produce the spooled CRPOP report of open, larger-quantity lines; check the DSPLY summary CRPORPT LINES=n TOTAL=v and review the spool with WRKSPLF.

Post-checks after CRPORPT:

E.3 Failure & re-run rules

SituationBehaviourAction
Out-of-list supplier at PO entryDSPF-level VALUES rejects it before the program runs; “not valid” message, nothing written.Re-key with one of S0001S0004.
Missing PONO or supplierProgram sets *IN40 (ALARM), shows the required-field message, order not accepted, banner stays “NEW PO”.Supply both required fields and press Enter.
Blank item on the add-line rowLine is skipped (no CRPOL row); the header Enter still processes.Expected — use it to submit the header alone, then add lines.
Re-run a reportReports are read-only; identical output each time.Safe to re-run any number of times — fully idempotent.
Empty filesReports print/DSPLY zero rows (no error).Seed master/PO data first.
Rebuild over existing objectsCRBUILD re-issues the CRT* commands.Delete the objects first (or rebuild into a clean library) to avoid already-exists conditions.
Because CRAFTORD/i has no posting or period-close cycle, there is no partial-update or re-post hazard: the only writer is the interactive CRPOENT (which writes a whole order on F3), and both reports are read-only.

F. Developer Reference ↑ top

CRAFTORD/i's reason for existing is the DDS. This section maps each DDS feature to the object that demonstrates it, summarises the three programs, and records the honest own-app findings.

F.1 DDS feature matrix

DDS featureObjectWhere / how
Float field (type F, 8-byte)CRITEMWEIGHT 9F 4 FLTPCN(*DOUBLE) — genuine IEEE double; round-trips through the store, not RPG-readable.
Variable-length fieldCRITEMNOTES 60A VARLEN(20) — 2-byte length prefix in the stored layout.
Reference field (REFFLD)CRITEM / CRPO / CRPOLSUPID/ITEMNO R REFFLD(... ) — inherits type/len and validity.
VALUES validityCRSUPPSUPID and STATE each carry a separate VALUES(); CRPO.POSTAT too.
RANGE validityCRSUPPRATING 1S 0 RANGE(1 5).
CHECK(ME) mandatory entryCRSUPPTERMS 4A CHECK(ME).
Reference inheritance of validityCRPOD (HSUPID)HSUPID R REFFLD(SUPID CRSUPP) inherits the supplier VALUES, enforced at the display-file level.
LF RENAMECRITMDLINO RENAME(ITEMNO).
LF DESCEND access pathCRITMDLK UNITPR DESCEND (read most-expensive first).
Join LF (JFILE/JOIN/JFLD/JREF)CRPOJLJFILE(CRPOL CRPO), joined on PONO, fields JREF'd from each side.
Join DESCEND key + secondary keyCRPOJLK LNPRICE DESCEND, K PONO.
AND-connected select/omitCRPOJLS POSTAT COMP(EQ 'O') ANDed with QTY COMP(GT 5), then O ALL.
Subfile (SFL/SFLCTL)CRPODPSFL/PCTL, SFLSIZ/SFLPAG/ROLLUP/ROLLDOWN, SFLDSP/SFLDSPCTL/SFLCLR/SFLEND.
OR-conditioning (indicator groups)CRPODN10 N11 AND-group vs. 10 O 11 OR-group banner.
DATE / TIME / USER specialsCRPODpositioned on the header; DATE with EDTCDE(Y).
EDTCDE modifierCRPODSPRICE EDTCDE(1 $) floating-dollar on a subfile column.
CHECK(RB) right-adjustCRPODHREF 10A CHECK(RB).
ALARMCRPOD40 ALARM on a rejected entry.
PRTF edit word (asterisk-protect)CRPOPRLINE EDTWRD(' * . ').
PRTF EDTCDE(Y) dateCRPOPRDATE 8S 0 EDTCDE(Y).
PRTF OR-conditioned constantsCRPOP21 'HIGH VALUE LINE' / N21 (see F.3).
PRTF page overflowCRPOP + CRPORPTOFLIND(*IN90), re-emit header on overflow.

F.2 Programs (3 RPG + 1 CL)

CRWTRPT (RPGLE)
Fixed-form + /free. Opens CRITMDL keyed, SETLL *LOVAL then a READ loop (LR on *IN91), DSPLYing INO and UNITPR per record and a final count — proves the DESCEND path and live RENAME.
CRPORPT (RPGLE)
Opens CRPOJL keyed and CRPOP as a printer with OFLIND(*IN90). Writes PHDR/PCOL, then *HIVAL SETGT + forward READ loop over the descending join; per row it maps fields to the PDTL output, sets *IN21 when LNPRICE>30, accumulates the total, writes PDTL, and re-emits the header on overflow. Ends with PTOT (count via %editc(:'Z'), total via %editc(:'K')) and a DSPLY summary.
CRPOENT (RPGLE)
Workstation file CRPOD with SFILE(PSFL:RRN); update-add CRPO/CRPOL. EXFMT PCTL loop until *IN03 (F3). On the first pass it enforces required PONO/HSUPID (else *IN40 ALARM + iter), starts the sequence and turns on *IN10 (banner flip). Each pass, if IITEM is non-blank, it copies the entry fields into the subfile row, WRITE PSFL, then WRITE POLREC. On exit it sets the header fields and WRITE POHREC.
CRBUILD (CLLE)
The pure-CL build script (Section C.1): CRTPF×4, CRTLF×2, CRTDSPF, CRTPRTF, CRTBNDRPG×3, then SNDPGMMSG.

F.3 Known own-app findings (honest)

Because CRAFTORD/i is a QA vehicle, its source records several findings against its own earlier drafts and against the platform. These are documented, not hidden:

DDS-QA-04 — PRTF conditioning indicators on constants (CONFIRMED platform bug)
The 21 'HIGH VALUE LINE' / N21 ' ' conditioned constants on PDTL are not honored: the platform's print path emits constants unconditionally (ignoring the conditioning indicators), so the flag prints on every line and, since both constants share column 56, the later one overwrites the first two characters of the earlier one. The intended behaviour is exactly one flag, on the high-value line only. The battle test asserts the observed buggy outcome (two corrupted flag lines), explicitly recorded as a confirmed bug, not as if fixed.
DDS-QA-05 — OR-connected banner must be one field, not two (own-app fix)
An earlier draft OR-connected two separate constant lines ('NEW PO' / 'EXISTING PO'); the platform correctly refused to treat two independent field specs as one OR-group. The shipped DDS uses a single OR-conditioned field (10 O 11) plus a separate AND-conditioned constant, which is the correct DDS form.
DDS-QA-06 — subfile input fields must be WRITTEN before they exist (own-app fix)
An earlier draft made the subfile columns (SITEM/SQTY/SPRICE) directly keyable, expecting to type into “the next blank subfile row.” That fails because a subfile record's input fields only exist once the record is WRITTEN. The shipped design puts the entry fields on the always-present PCTL control record and copies them into each new subfile row.
Float fields are not RPG-readable (documented boundary, SNF5002)
CRITEM.WEIGHT (type F) round-trips through the data store but cannot be read by a compiled RPG program; CRITMDL therefore keys its DESCEND path on UNITPR instead. This is an honest platform boundary, not a defect.

G. Glossary ↑ top

DDS — Data Description Specifications
The column-oriented source language that describes IBM i files: physical files, logical files, display files, and printer files. CRAFTORD/i is built almost entirely from DDS.
PF / LF (physical / logical file)
A PF holds the actual records (CRSUPP/CRITEM/CRPO/CRPOL); an LF is an alternate view/access path over one or more PFs (CRITMDL, and the join LF CRPOJL).
Join logical file
An LF that combines fields from two or more PFs into one record format via JFILE/JOIN/JFLD/JREF. Here CRPOJL joins PO lines to their header.
REFFLD / reference inheritance
Defining a field by reference to another field so it inherits type, length and validity keywords. CRITEM.SUPID, CRPO.SUPID, CRPOL.ITEMNO and the screen's HSUPID are all reference fields.
VALUES / RANGE / CHECK
DDS validity keywords: VALUES restricts to a list, RANGE to a numeric span, CHECK(ME) requires entry, CHECK(RB) right-adjusts. Enforced at the file/display level and inheritable by reference.
Float field (FLTPCN)
A DDS floating-point field (type F). FLTPCN(*DOUBLE) makes it an 8-byte IEEE double. Stored/retrieved by the platform but not readable by compiled RPG.
VARLEN
A variable-length character field, stored with a length prefix so only the used bytes are kept.
Subfile (SFL / SFLCTL)
A 5250 display construct listing many rows on one screen. CRPOD's PSFL subfile shows the PO lines as they are keyed; its PCTL control record carries the entry fields.
OR-conditioning
Combining multiple indicator lines of one field with an O in the type column so the field shows when any of the indicator sets is true. The “EXISTING PO” banner is an OR-conditioned field.
DATE / TIME / USER specials
DDS keywords that render the current date, time and job user directly on a screen or report, without program code.
EDTCDE / EDTWRD
Editing controls for numeric output. EDTCDE applies a standard edit (e.g. Y = mm/dd/yy, 1 $ = floating dollar); EDTWRD is a custom edit word (here an asterisk-protect mask on the money column).
DESCEND access path
A keyed file whose records are sequenced high-to-low on the key, so forward reads return the largest key first. Used by CRITMDL (UNITPR) and CRPOJL (LNPRICE).
Select / omit
LF rules that include (S) or exclude (O) records by field comparison. CRPOJL selects POSTAT='O' AND QTY>5 and omits the rest.
ALARM
A DSPF keyword that sounds the terminal alarm when its conditioning indicator is on — here on a rejected PO entry.
OFLIND (overflow indicator)
A printer-file indicator the platform turns on at page overflow, letting the program re-emit headings on each new page.
SBMJOB
Submit Job — the IBM i command that queues a program to run as a batch job (e.g. SBMJOB CMD(CALL PGM(CRAFTORD/CRPORPT))).