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).
CRAFTORD/i manages the purchasing side of a craft-supplies wholesaler:
CRSUPP) and items (CRITEM), where an
item carries a per-unit price, a floating-point shipping weight, and a variable-length notes field,
and references its supplier.CRPO: supplier, date, status
open/closed/cancelled) and one or more PO lines (CRPOL: item, quantity, line price).CRPOENT program presents the
CRPOD subfile screen, where an operator keys a PO header and adds line after line,
each landing in the subfile and being written to CRPOL.CRWTRPT walks the CRITMDL
logical file, whose descending access path over unit price lists the most expensive item first.CRPORPT reads the CRPOJL join
logical file (PO header joined to its lines, filtered to open, larger-quantity lines) and prints it
through the CRPOP printer file.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).
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.
| Object | Type | Role |
|---|---|---|
| CRSUPP | PF | Supplier master (source of the validity keywords others inherit). |
| CRITEM | PF | Item master (float WEIGHT, VARLEN NOTES, REFFLD SUPID). |
| CRPO | PF | Purchase-order header. |
| CRPOL | PF | Purchase-order line. |
| CRITMDL | LF | Simple LF over CRITEM: RENAME + DESCEND access path. |
| CRPOJL | Join LF | CRPOL ⨯ CRPO join, DESCEND key, AND select/omit. |
| CRPOD | DSPF | Subfile PO-entry screen (SFL/SFLCTL). |
| CRPOP | PRTF | PO detail report (edit words, OR conditions, overflow). |
| CRWTRPT | RPGLE | Heaviest-priced-item report (drives CRITMDL). |
| CRPORPT | RPGLE | PO detail report (drives CRPOJL + CRPOP). |
| CRPOENT | RPGLE | Interactive PO-entry program (drives CRPOD). |
| CRBUILD | CLLE | Build 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.
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 this | Type on the command line |
|---|---|
| Open the interactive PO-entry screen | CALL CRAFTORD/CRPOENT |
| Run the heaviest-priced-item report | CALL CRAFTORD/CRWTRPT (or SBMJOB it) |
| Run the PO detail report (spooled) | CALL CRAFTORD/CRPORPT (or SBMJOB it) |
| (Re)build every object from source | CALL 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.
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.
| Field | Type (DDS) | Role |
|---|---|---|
| PONO | 6A input/output | Purchase-order number keyed by the operator (required). |
| HSUPID | REF (5A) input/output | Supplier code; REFFLD(SUPID CRSUPP) so it inherits the VALUES('S0001' 'S0002' 'S0003' 'S0004') validity list (required). |
| HREF | 10A input/output, CHECK(RB) | Free-text PO reference code, stored/rendered right-adjusted. |
| IITEM | 6A input/output | Item number for the next line to add. |
| IQTY | 5S,0 input/output | Quantity for the next line. |
| IPRICE | 7S,2 input/output | Line price for the next line. |
| DMSG | 50A output, DSPATR(HI) | Highlighted message line (row 22) for validation feedback. |
| Field | Type (DDS) | Shows |
|---|---|---|
| SLSEQ | 2S,0 output | Line sequence within the order. |
| SITEM | 6A output | Item number of the accepted line. |
| SQTY | 5S,0 output | Quantity. |
| SPRICE | 7S,2 output, EDTCDE(1 $) | Line price with the floating-dollar edit-code modifier (e.g. $12.50). |
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.IITEM, IQTY, IPRICE on the
“Add line” row and press Enter. The program bumps the sequence,
copies the values into a new PSFL subfile row, WRITEs the subfile record,
then WRITEs the matching CRPOL line record. A blank IITEM is
skipped (no line added), which lets the operator press Enter on the header alone.PONO and HSUPID are
required; when they are supplied the header is accepted, the sequence starts, and the banner flips
from “NEW PO” to “EXISTING PO” (see B.3).ROLLUP(25)/ROLLDOWN(26), SFLPAG(0005) lines per page).CA03(03)). On the way out
CRPOENT writes the CRPO header row (supplier from HSUPID,
status 'O', a fixed PO date), completing the order.CRAFTORD/i's PO-entry screen demonstrates three distinct DDS-driven behaviours the operator sees directly:
HSUPID is
REFFLD(SUPID CRSUPP), so it inherits CRSUPP.SUPID's
VALUES('S0001' 'S0002' 'S0003' 'S0004'). An out-of-list code (e.g. ZZZZZ)
is refused by the display file before the RPG program body runs again — the screen
redisplays with a “not valid” message and nothing is written to the database. This is
the same inherited-validity mechanism the item and PO files rely on.PONO or HSUPID is
blank, CRPOENT sets its own message on DMSG (“PO number and supplier
are required.”), turns on *IN40, and iterates — the DSPF's
40 ALARM condition rings the terminal alarm and the screen redisplays. The order is not
accepted and the banner stays “NEW PO.”N10 N11 (shows only while both
indicators are off). “EXISTING PO” is a genuine OR-conditioned field: an
indicator line naming 10, then an O-connected line naming 11
that also carries the text. *IN10 starts off, so the first screen shows “NEW
PO.” Once the header is accepted *IN10 turns on and the banner flips to
“EXISTING PO” for every screen after. A rejected submission never turns on
*IN10, so the banner correctly stays “NEW PO” on a validation failure.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.
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).
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.
| Program | Purpose | Reads | Output |
|---|---|---|---|
| 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: CRPOL ⨯ CRPO, 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 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
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 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
CRBUILD must run (and, before it, the source must be
seeded by seedCraftord()) so the files and programs exist. Within the build, PFs
precede LFs precede programs.CRWTRPT needs items in
CRITEM; CRPORPT needs headers in CRPO and lines in
CRPOL. With no data the reports simply print/DSPLY zero rows.CRPOENT writes PO data.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.
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.
| Field | Type | Validity / keyword | Meaning |
|---|---|---|---|
| SUPID | 5A (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. |
| SUPNAME | 25A | — | Supplier name. |
| STATE | 2A | VALUES('CA' 'OR' 'WA' 'NV') | State — a separate VALUES list. |
| RATING | 1S,0 | RANGE(1 5) | Supplier rating 1–5. |
| TERMS | 4A | CHECK(ME) | Payment terms — mandatory entry. |
Seeded suppliers: S0001 Pacific Fiber Mill (OR, rating 4, N30) and
S0002 Sierra Bead Works (CA, rating 5, N60).
Carries the two most distinctive PF field types in the app: a floating-point field and a variable-length field, plus a referenced field.
| Field | Type | Keyword | Meaning |
|---|---|---|---|
| ITEMNO | 6A (key) | — | Item number, e.g. I00001. |
| DESC | 20A | — | Item description. |
| SUPID | REF (5A) | REFFLD(SUPID CRSUPP) | Owning supplier — inherits CRSUPP.SUPID's type, length and VALUES list. |
| UNITPR | 7S,2 | — | Per-unit price (the RPG-visible field CRITMDL keys on). |
| WEIGHT | 9F,4 | FLTPCN(*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). |
| NOTES | 60A | VARLEN(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.
| Field | Type | Keyword | Meaning |
|---|---|---|---|
| PONO | 6A (key) | — | Purchase-order number, e.g. PO0001. |
| SUPID | REF (5A) | REFFLD(SUPID CRSUPP) | Supplier (inherits VALUES). |
| PODATE | 8S,0 | — | PO date, YYYYMMDD. |
| POSTAT | 1A | VALUES('O' 'C' 'X') | Status: O open, C closed, X cancelled. Drives the join LF's select rule. |
| Field | Type | Keyword | Meaning |
|---|---|---|---|
| PONO | 6A (key) | — | Owning PO number. |
| LSEQ | 2S,0 (key) | — | Line sequence within the order. |
| ITEMNO | REF (6A) | REFFLD(ITEMNO CRITEM) | Item ordered (inherits from CRITEM.ITEMNO). |
| QTY | 5S,0 | — | Quantity. Drives the join LF's QTY>5 select clause. |
| LNPRICE | 7S,2 | — | Line 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.A single-format logical file (PFILE(CRITEM)) that demonstrates two features at once:
ITEMNO is exposed as INO (RENAME(ITEMNO));
the compiled program reads the field as INO, and ITEMNO no longer appears.K UNITPR ... DESCEND, so sequential forward
reads return records highest-price first. It exposes INO, DESC, SUPID, UNITPR.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.
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
CRPOL to CRPO on
PONO. CRPOL is named first because the key fields (including the DESCEND
key LNPRICE) must come from the first-named JFILE file.JREF(1) = CRPOL,
JREF(2) = CRPO.LNPRICE (secondary key PONO) — kept rows come
out most-expensive-line first.S) rule requires
POSTAT='O' and QTY>5 (the second line, with no S/O in the
type column, ANDs onto the first). A trailing O ALL omits everything the select rule
did not catch. Note select/omit fields are not restricted to the first file the way key
fields are — POSTAT comes from CRPO (JREF 2).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.
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:
SFLSIZ(0010)/SFLPAG(0005),
ROLLUP(25)/ROLLDOWN(26), and the conditioned control keywords
N31 SFLDSP, N31 SFLDSPCTL, 31 SFLCLR,
90 SFLEND(*MORE).N10 N11 “NEW PO” AND-group and the
10 O 11 “EXISTING PO” OR-group (see B.3).DATE EDTCDE(Y), TIME, USER.SPRICE ... EDTCDE(1 $) (floating-dollar) on a subfile
column.HSUPID R ... REFFLD(SUPID CRSUPP)
inherits the supplier VALUES list, validated at the display-file level.HREF ... CHECK(RB) right-adjusts input.40 ALARM rings when the program sets *IN40 on a
rejected entry.Four records driving the spooled report from CRPORPT:
| Record | Keyword(s) | Role |
|---|---|---|
| PHDR | SKIPB(001) SPACEA(002), HIGHLIGHT | Report title banner. |
| PCOL | SPACEA(001) | Column headings. |
| PDTL | SPACEB(001) | One detail line per join row. |
| PTOT | SPACEB(002) | Lines-printed count and total line value. |
Notable detail fields on PDTL:
RDATE 8S,0 EDTCDE(Y) — the PO date edited to mm/dd/yy with slashes.RLINE 7S,2 EDTWRD(' * . ') — an asterisk-protect edit word on
the money column: the leading digit slots before the first significant digit are filled with
asterisks, so 34.75 prints **034.75 and 9.00 prints **009.00.21 'HIGH VALUE LINE' / N21 ' ' — an OR-conditioned two-line
status flag intended to appear only on high-value lines. (This exposes a confirmed platform finding
— see F.3, DDS-QA-04.)The file is opened with OFLIND(*IN90) so CRPORPT can detect page overflow and
re-emit the header/column records.
CRITEM.SUPID → CRSUPP.SUPID (REFFLD; inherits VALUES validity).CRPO.SUPID → CRSUPP.SUPID (REFFLD).CRPOL.ITEMNO → CRITEM.ITEMNO (REFFLD).CRPOL.PONO → CRPO.PONO (line to header; the join key in CRPOJL).CRITMDL is a keyed alternate access path over CRITEM (RENAME + DESCEND UNITPR).CRPOJL joins CRPOL (primary) and CRPO on PONO.CRPOD's HSUPID and CRPOP's output fields reference the PF fields above.CRAFTORD and CURLIB=CRAFTORD.CRTLIB LIB(CRAFTORD).QDDSSRC/QRPGLESRC/QCLSRC — done programmatically by seedCraftord().CRTCLPGM PGM(CRAFTORD/CRBUILD) SRCFILE(CRAFTORD/QCLSRC) SRCMBR(CRBUILD).CALL PGM(CRAFTORD/CRBUILD). Watch for CRAFTORD build complete.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.CALL CRAFTORD/CRWTRPT for a most-expensive-first item list
(verify the DSPLY ends CRWTRPT DONE n with n = item count).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:
CRPOL rows whose PO is open and whose QTY>5.LNPRICE order.**034.75); the date shows mm/dd/yy.| Situation | Behaviour | Action |
|---|---|---|
| Out-of-list supplier at PO entry | DSPF-level VALUES rejects it before the program runs; “not valid” message, nothing written. | Re-key with one of S0001–S0004. |
| Missing PONO or supplier | Program 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 row | Line is skipped (no CRPOL row); the header Enter still processes. | Expected — use it to submit the header alone, then add lines. |
| Re-run a report | Reports are read-only; identical output each time. | Safe to re-run any number of times — fully idempotent. |
| Empty files | Reports print/DSPLY zero rows (no error). | Seed master/PO data first. |
| Rebuild over existing objects | CRBUILD re-issues the CRT* commands. | Delete the objects first (or rebuild into a clean library) to avoid already-exists conditions. |
CRPOENT (which writes a whole order on
F3), and both reports are read-only.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.
| DDS feature | Object | Where / how |
|---|---|---|
| Float field (type F, 8-byte) | CRITEM | WEIGHT 9F 4 FLTPCN(*DOUBLE) — genuine IEEE double; round-trips through the store, not RPG-readable. |
| Variable-length field | CRITEM | NOTES 60A VARLEN(20) — 2-byte length prefix in the stored layout. |
| Reference field (REFFLD) | CRITEM / CRPO / CRPOL | SUPID/ITEMNO R REFFLD(... ) — inherits type/len and validity. |
| VALUES validity | CRSUPP | SUPID and STATE each carry a separate VALUES(); CRPO.POSTAT too. |
| RANGE validity | CRSUPP | RATING 1S 0 RANGE(1 5). |
| CHECK(ME) mandatory entry | CRSUPP | TERMS 4A CHECK(ME). |
| Reference inheritance of validity | CRPOD (HSUPID) | HSUPID R REFFLD(SUPID CRSUPP) inherits the supplier VALUES, enforced at the display-file level. |
| LF RENAME | CRITMDL | INO RENAME(ITEMNO). |
| LF DESCEND access path | CRITMDL | K UNITPR DESCEND (read most-expensive first). |
| Join LF (JFILE/JOIN/JFLD/JREF) | CRPOJL | JFILE(CRPOL CRPO), joined on PONO, fields JREF'd from each side. |
| Join DESCEND key + secondary key | CRPOJL | K LNPRICE DESCEND, K PONO. |
| AND-connected select/omit | CRPOJL | S POSTAT COMP(EQ 'O') ANDed with QTY COMP(GT 5), then O ALL. |
| Subfile (SFL/SFLCTL) | CRPOD | PSFL/PCTL, SFLSIZ/SFLPAG/ROLLUP/ROLLDOWN, SFLDSP/SFLDSPCTL/SFLCLR/SFLEND. |
| OR-conditioning (indicator groups) | CRPOD | N10 N11 AND-group vs. 10 O 11 OR-group banner. |
| DATE / TIME / USER specials | CRPOD | positioned on the header; DATE with EDTCDE(Y). |
| EDTCDE modifier | CRPOD | SPRICE EDTCDE(1 $) floating-dollar on a subfile column. |
| CHECK(RB) right-adjust | CRPOD | HREF 10A CHECK(RB). |
| ALARM | CRPOD | 40 ALARM on a rejected entry. |
| PRTF edit word (asterisk-protect) | CRPOP | RLINE EDTWRD(' * . '). |
| PRTF EDTCDE(Y) date | CRPOP | RDATE 8S 0 EDTCDE(Y). |
| PRTF OR-conditioned constants | CRPOP | 21 'HIGH VALUE LINE' / N21 (see F.3). |
| PRTF page overflow | CRPOP + CRPORPT | OFLIND(*IN90), re-emit header on overflow. |
/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.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.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.SNDPGMMSG.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:
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.10 O 11) plus a separate AND-conditioned constant, which
is the correct DDS form.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.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.CRPOJL joins PO lines to their header.CRITEM.SUPID, CRPO.SUPID, CRPOL.ITEMNO and the screen's
HSUPID are all reference fields.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.FLTPCN(*DOUBLE) makes it an 8-byte IEEE double.
Stored/retrieved by the platform but not readable by compiled RPG.PSFL subfile shows the
PO lines as they are keyed; its PCTL control record carries the entry fields.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.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).CRITMDL (UNITPR) and CRPOJL (LNPRICE).POSTAT='O' AND QTY>5 and omits the rest.SBMJOB CMD(CALL PGM(CRAFTORD/CRPORPT))).