FORGE/i is a discrete-manufacturing MRP core: an item master with a multi-level bill of
materials, work orders that explode the BOM and allocate components, shop-floor material issue and
completion receipt, an accumulating inventory ledger, a weekly MRP regeneration with reorder
suggestions and ABC re-ranking, and a monthly close that rolls standard cost up through the BOM, values
work-in-process, and posts a balanced feed into the general ledger. It runs on four job cycles
— daily, weekly, monthly and annual — each a CL that chains a set of RPG/COBOL programs.
This manual is the reference for the operator who runs the online inquiry screens and the periodic
cycles, and for the developer maintaining the application. It is grounded entirely in the committed
source (mrp-app/src/sources.mjs for library FORGE, src/seed.mjs,
and the test/forge_*.mjs drivers).
FORGE/i plans and tracks the manufacture of discrete goods — a pump family built from sub-assemblies and raw parts:
FGITEM) typed
F finished / S sub-assembly / R raw; that letter drives whether MRP explodes
the item through its bill of materials (FGBOM) or treats it as a purchase.FGWOMAT), the components are allocated on the item master, and the order flips to
released — with a scrap uplift applied per component.FGITRN).Honest statement: FORGE/i is a classic RPG-over-DDS application — unlike LOANSVC/i it does not push its business logic down into a DB2 SQL-PL layer. The manufacturing rules (BOM explosion, scrap uplift, netting, cost roll-up) live in the RPG programs themselves, operating directly on keyed physical files. SQL appears in exactly two places, both deliberate:
FGGLFEED) is an SQL table created by RUNSQLSTM, and
the monthly GL-post program FGGLPST is the one SQLRPGLE member — it reads
the ledger with native I/O but writes the GL with embedded EXEC SQL INSERT.FGGLRPT) is hand-written ILE COBOL reading the ledger's
keyed side, proving the month's postings.Everything else — 12 further RPG programs, 3 display files, 1 printer file, 8 physical files and
2 logical files — is native RPG/DDS. The whole application lives in library FORGE;
FGSETUP (CL) creates every object and compiles every program, and the four cycle CLs drive
them. The tested jobs run with LIBL = QSYS QGPL FORGE QTEMP and CURLIB = FORGE.
ONLINE DAILY (FGDAILY) WEEKLY (FGWEEK)
------ --------------- ---------------
FGMENU (5250) FGWOREL explode+alloc FGMRPGEN net demand -> FGMRP
1 -> FGITEMIQ FGISSUE draw material FGABC re-rank A/B/C by value
2 -> FGWOIQ (subfile) FGRECPT receive done FGMRPPR spool suggestion report
MONTHLY (FGMONTH) ANNUAL (FGYEAR)
----------------- ---------------
FGCSTRL roll std cost up BOM FGPHYS physical-count variance
FGWIPVL value work-in-process FGCSTRL re-roll cost year
FGGLPST post balanced GL feed FGGLRPT trial balance
FGGLRPT trial balance (COBOL)
+----------------------------------------------+
FGITEM (item+BOM spine) <--reads/updates-- every program |
| FGBOM / FGBOMLF (parent/component access paths) |
| FGWO / FGWOLF <-- FGWOMAT (order material lines) |
+-> FGITRN (accumulating inventory ledger: I/R/A/V) <--------+
FGMRP (weekly plan rows) FGCSTH (cost-change history)
FGGLFEED (SQL GL feed) FGWKCTR (work centres)
A single event — say a daily material issue — flows: FGISSUE reads each open
FGWOMAT line → CHAINs the component in FGITEM, relieving ONHAND
and ALLOC → writes one FGITRN issue row under a stable ledger key →
flips the line to issued. No control table gates the run; idempotency comes from the ledger key
(section F.3), so a re-submitted cycle re-derives the same key and is refused rather than
double-posting.
| Object | Type | Role |
|---|---|---|
| FGITEM | PF | Item master — the spine of the whole system. |
| FGBOM | PF | Bill of materials (parent→component, per-unit qty). |
| FGBOMLF | LF | BOM keyed by COMPNO — where-used access path. |
| FGWO | PF | Work-order header. |
| FGWOLF | LF | Work orders keyed by status. |
| FGWOMAT | PF | Work-order material lines (the explosion result). |
| FGITRN | PF | Inventory transaction ledger (durable audit trail). |
| FGMRP | PF | MRP planning results, one row per item per run. |
| FGCSTH | PF | Standard-cost change history. |
| FGWKCTR | PF | Work-centre master. |
| FGGLFEED | SQL table | General-ledger feed the monthly close posts into. |
| FGITEMD / FGWOD / FGMENUD | DSPF | Item inquiry / work-order subfile / menu. |
| FGMRPP | PRTF | MRP suggestion report printer file. |
| FGREFLD | RPGLE | Seeds reference data (items, BOM, work centres). |
| FGWOREL | RPGLE | Release WOs: explode BOM + allocate. |
| FGISSUE / FGRECPT | RPGLE | Daily material issue / completion receipt. |
| FGMRPGEN / FGABC / FGMRPPR | RPGLE | Weekly MRP regen / ABC re-rank / report. |
| FGCSTRL / FGWIPVL | RPGLE | Cost roll-up / WIP valuation (monthly). |
| FGPHYS | RPGLE | Annual physical-count variance. |
| FGGLPST | SQLRPGLE | Monthly GL post (embedded SQL). |
| FGITEMIQ / FGWOIQ / FGMENU | RPGLE | Interactive item / work-order / menu programs. |
| FGGLRPT | CBLLE | GL trial-balance report (ILE COBOL). |
| FGSETUP | CLP | Create every object, compile every program. |
| FGDAILY / FGWEEK / FGMONTH / FGYEAR | CLP | The four job cycles. |
The full catalogue is 8 PFs + 2 LFs + 1 SQL table, 3 DSPFs + 1 PRTF, 14 RPG programs (13 RPGLE +
1 SQLRPGLE), 1 COBOL program and 5 CL programs, all in library FORGE. Sections D and F
expand each.
FORGE/i has no CICS transaction identifiers and no menu-driven transid switch. On IBM i, each
program is reached by name from a 5250 command-entry line (or a JOBQ/scheduler for the batch
cycles). The operator equivalent of "type a transid and Enter" is "type a CALL command and
Enter". Before invoking anything, the job's library list must include FORGE.
| To do this | Type on the command line |
|---|---|
| Open the main menu (routes to the two inquiries) | CALL FORGE/FGMENU |
| Open item inquiry directly | CALL FORGE/FGITEMIQ |
| Open work-order inquiry (subfile) directly | CALL FORGE/FGWOIQ |
| Build/compile the whole application | CALL FORGE/FGSETUP |
| Seed reference data (items, BOM, work centres) | CALL FORGE/FGREFLD |
| Run the daily shop-floor cycle | CALL FORGE/FGDAILY (or SBMJOB it) |
| Run the weekly planning cycle | CALL FORGE/FGWEEK |
| Run the monthly close | CALL FORGE/FGMONTH |
| Run the annual inventory/cost-year roll | CALL FORGE/FGYEAR |
Every batch program takes no CALL parameters. There is no LOANSVC-style control table:
processing dates are compiled constants in the source (for example the daily issue posts
TDT = 20260801, MRP stamps RUNNO = 202631, the monthly GL batch is
202608). A scheduled submission is therefore a bare CALL. Only
FGMENU, FGITEMIQ and FGWOIQ are interactive; the batch programs
run to completion and DSPLY a one-line result.
FORGE/i ships three interactive programs. FGMENU is the entry point; it CALLs the two
inquiries program-to-program and returns to the menu when they exit.
Key 1 or 2 and Enter; FGMENU CALLs
FGITEMIQ or FGWOIQ. Any other non-blank option shows
Invalid option. on the menu itself. F3 ends the program.
A plain (non-subfile) screen. Key an item number, press Enter; the program
CHAINs FGITEM and renders the master record plus a derived available-to-promise
figure (ONHAND − ALLOC). A miss shows Item not found. and clears the
detail rather than leaving stale values.
The available figure is genuinely derived, not stored: with two released orders drawing
10 + 10 units of the housing, 100 on-hand − 20 allocated renders as 80.00. The packed 11P4
standard cost renders at full precision (42.5000).
The app's subfile screen (DDS record WSFL under control record WCTL,
SFLPAG(5) per page, SFLSIZ(20), ROLLUP/ROLLDOWN paging). Key a work order,
Enter; the header (item, ordered, completed) renders and the order's material
lines load into the subfile.
EXFMT that read the operator key — never before it. A second enquiry against a
work order with a different BOM redraws the correct number of rows, and an unknown work order clears the
subfile to zero rows (SFLCLR really fires). This is documented directly in the program source
as the discipline that keeps the previous enquiry's rows from bleeding into the next.Honest statement: FORGE/i does not model a true four-eyes maker–checker /
separate-authorization workflow, and the online screens are read-only inquiries — neither
FGITEMIQ nor FGWOIQ updates a file. All state change happens in the batch
cycles. The manual documents the control model the application does have:
I),
completion receipt (R), physical-count adjustment (A), cost revalue
(V) — is journaled with the item, signed quantity, extended cost, a work-order or
source reference and a memo. This is the durable, reconstructable audit trail; the cycles test
verifies every ledger key is unique across all four cycles.FGWOREL skips any WO not
Planned; FGISSUE skips a line not Open; FGRECPT processes
only Released/in-Work orders; FGMRPGEN plans only Active items;
FGPHYS counts only active class-A items.FGGLPST posts matched
debit/credit pairs (raw→WIP on issue, WIP→FG on receipt), and the COBOL
FGGLRPT re-reads the ledger to prove the totals — a second, independent read of
the same data.In sum, the control posture is an accumulating ledger + cost history + status/state gating + key-based idempotency + a balanced, independently-proved GL, rather than a segregation-of-duties approval workflow.
FORGE/i's processing runs as four periodic cycles, each a CL program that chains a fixed set of
RPG/COBOL programs and ends with a completion banner. All programs take no CALL parameters and
read no control table — a scheduled/JOBQ submission is a bare CALL of the cycle CL.
-- submit the (parameterless) daily cycle SBMJOB CMD(CALL PGM(FORGE/FGDAILY)) JOB(FGDAILY) -- or run interactively CALL FORGE/FGWEEK
| Program | Cycle | Purpose | Reads | Writes / effect | DSPLY result |
|---|---|---|---|---|---|
| FGWOREL | Daily | Release planned WOs: explode BOM one level, allocate components. | FGWO, FGBOM, FGITEM | FGWOMAT lines; FGITEM.ALLOC += qty; WOSTAT P→R, RELDT set. | FGWOREL RELEASED=n SKIP=n |
| FGISSUE | Daily | Issue material to released orders. | FGWOMAT, FGITEM, FGITRN | FGITEM ONHAND−/ALLOC−; FGITRN 'I' row (neg qty/cost); MSTAT O→I. | FGISSUE ISSUED=n SKIP=n |
| FGRECPT | Daily | Receive WO completions into stock at standard cost. | FGWO, FGITEM, FGITRN | FGITEM ONHAND+; FGITRN 'R' row (pos); WORCVD advanced; WOSTAT W or C. | FGRECPT RECEIVED=n SKIP=n |
| FGMRPGEN | Weekly | MRP regeneration: net demand, suggest replenishment. | FGITEM, FGWOMAT, FGMRP | FGMRP row per active item (GROSSRQ/NETREQ/SUGQTY/MSGCD). | FGMRPGEN RUN=n PLANNED=n SKIP=n |
| FGABC | Weekly | Re-rank items A/B/C by extended inventory value. | FGITEM | FGITEM.ABCCLS set (≥2000=A, ≥500=B, else C). | FGABC A=n B=n C=n |
| FGMRPPR | Weekly | Spool the MRP suggestion report (page overflow). | FGMRP, FGITEM | Spooled printout (FGMRPP): one line per suggested order. | FGMRPPR LINES=n |
| FGCSTRL | Monthly / Annual | Standard-cost roll-up: sub-assemblies then finished goods. | FGITEM, FGBOM, FGCSTH | FGITEM.STDCOST updated; FGCSTH row per change (before/after). | FGCSTRL COSTED=n |
| FGWIPVL | Monthly | Value work-in-process from issued material. | FGWO, FGWOMAT, FGITEM | FGWO.WOCOST set for R/W orders. | FGWIPVL ORDERS=n WIP=amt |
| FGGLPST | Monthly | Post a balanced DR/CR feed to the GL (SQLRPGLE). | FGITRN; FGGLFEED (SQL) | FGGLFEED rows: raw→WIP (issue), WIP→FG (receipt). | FGGLPST BATCH=n ROWS=n |
| FGGLRPT | Monthly / Annual | GL trial balance over the ledger (COBOL). | FGITRN | Printed totals (issues / receipts / txn count). | FGGLRPT ISSUES/RECEIPTS/TXNS |
| FGPHYS | Annual | Physical-count variance for class-A items. | FGITEM, FGITRN | FGITRN 'A' adjustment; FGITEM.ONHAND corrected to count. | FGPHYS ADJUSTED=n |
The cycle CLs chain these: FGDAILY = FGWOREL → FGISSUE → FGRECPT; FGWEEK = FGMRPGEN → FGABC → FGMRPPR; FGMONTH = FGCSTRL → FGWIPVL → FGGLPST → FGGLRPT; FGYEAR = FGPHYS → FGCSTRL → FGGLRPT.
FGWOREL reads planned orders, and for each explodes one BOM level into
FGWOMAT (a sub-assembly is a material line, not a second-level explosion), applying the
scrap uplift reqqty = qtyper × woqty × (1 + scrappc/100); it allocates each
component on FGITEM and flips the WO to released. FGISSUE then draws every open line,
relieving on-hand and allocation and posting a negative-quantity issue transaction. FGRECPT
receives the increment of completions since the last receipt (WOCOMP − WORCVD)
into finished stock, rolling the order to C when WOCOMP + WOSCRAP ≥ WOQTY,
else leaving it W.
Expected DSPLY (a 10-unit WO for FG100001, 4-line BOM, 6 then 4 received): FGWOREL RELEASED=1 SKIP=0 explode + allocate FGISSUE ISSUED=4 SKIP=0 4 material lines drawn FGRECPT RECEIVED=1 SKIP=0 this run's completion increment
Worked example (the daily test's oracle): a WO for 10×FG100001 explodes to housing
10.0000, shaft 10.2000 (10 × 1.02 scrap), sub-assembly 10.0000, seal 20.0000 (2 per unit). Issuing
the housing takes on-hand 100→90 and allocation 10→0, and posts an I row of
-10 qty / -425.00 cost (10 × 42.50).
FGMRPGEN plans every active item: WAVAIL = ONHAND − ALLOC − SAFETY,
gross requirement = sum of every open (MSTAT='O') work-order material line for that
component, NETREQ = max(0, GROSS − WAVAIL), suggested order = net; the message code is
OR (order), or SS if on-hand less allocation is already below safety stock.
FGABC re-ranks by ONHAND × STDCOST (≥2000→A, ≥500→B, else C).
FGMRPPR spools the suggestion report.
Expected DSPLY (7 active items planned; ABC re-rank): FGMRPGEN RUN=202631 PLANNED=7 SKIP=0 FGABC A=n B=n C=n FGMRPPR LINES=n one line per suggested order
SS. The housing (on-hand 100) suggests
nothing. FGMRP is keyed UNIQUE on (RUNNO, ITEMNO), so a re-run of the SAME week is
refused — a second FGMRPGEN reports PLANNED=0 and the file still holds
one row per item.FGCSTRL rolls standard cost bottom-up in two passes — pass 1 costs sub-assemblies
(ITYPE='S') from their components, pass 2 costs finished goods (ITYPE='F') from
theirs (now including the freshly-costed sub-assemblies), writing an FGCSTH row (reason
R) for each change. FGWIPVL values each R/W order at the standard cost of the
material issued against it. FGGLPST sums the ledger by type and posts balanced pairs into
FGGLFEED; FGGLRPT (COBOL) reads the ledger and prints the trial balance.
Expected cost roll-up (from the seeded BOM & raw costs):
SA200001 = 25.0000 + 6.2500 = 31.2500
FG100001 = 42.5000 + 19.1250(shaft@2% scrap)
+ 31.2500(sub-assembly) + 12.5000(2 seals)= 105.3750
FG100002 = 85.0000(2 housings) + 18.7500 = 103.7500
The GL posts 1310-RAW credit / 1320-WIP debit for issues and
1320-WIP credit / 1330-FG debit for receipts, so WIP appears on both sides and
SUM(DR) = SUM(CR). Both FGCSTRL and FGGLRPT also run in the annual cycle.
FGPHYS applies the run's deterministic physical-count convention — active class-A items
count 2 units short (shrinkage), everything else clean — writing an A adjustment
transaction (TQTY=-2) and correcting book on-hand to the counted quantity. FGCSTRL
re-rolls the cost year; FGGLRPT reprints the trial balance.
Expected DSPLY: FGPHYS ADJUSTED=n class-A items adjusted (-2 each)
ADJUSTED=0.MSTAT='O')
work-order material lines — those are created by FGWOREL and consumed (set to I)
by FGISSUE. Run the shop-floor cycle first so demand and stock are current before regenerating MRP.All files are in library FORGE, grounded in the DDS/SQL in sources.mjs.
Dates are stored as signed numeric in YYYYMMDD form. STDCOST is packed
11P4 (four decimals — a cost rolls through several BOM levels and rounding at 2dp per level
loses cents); BOM quantities are packed 9P4.
| Field | Type | Meaning |
|---|---|---|
| ITEMNO | 8A | Item number (key), e.g. FG100001. |
| IDESC | 30A | Description. |
| ITYPE | 1A | F finished, S sub-assembly, R raw — drives MRP explode vs purchase. |
| IUOM | 3A | Unit of measure. |
| STDCOST | 11P 4 | Standard cost (four decimals for multi-level roll-up). |
| ONHAND | 9P 2 | Book on-hand quantity. |
| ALLOC | 9P 2 | Quantity allocated to released work orders. |
| SAFETY | 9P 2 | Safety-stock level (MRP nets below this). |
| LEADTM | 3S 0 | Lead time, days. |
| ABCCLS | 1A | ABC class (re-ranked weekly by value). |
| ISTAT | 1A | Item status (A active). |
| Field | Type | Meaning |
|---|---|---|
| PARENT | 8A | Parent item (key 1). |
| BSEQ | 3S 0 | Line sequence (key 2) — deterministic explosion order. |
| COMPNO | 8A | Component item. |
| QTYPER | 9P 4 | Quantity of the component per unit of the parent. |
| SCRAPPC | 5P 2 | Scrap percent (uplifts required quantity). |
| BSTAT | 1A | BOM-line status (A active). |
FGBOMLF is a logical file over FGBOM keyed on COMPNO — the
where-used access path ("what does this part go into?"), non-unique by design (one component feeds many
parents).
| Field | Type | Meaning |
|---|---|---|
| WONO | 8A | Work-order number (key). |
| ITEMNO | 8A | Item being made. |
| WOQTY | 9P 2 | Quantity ordered. |
| WOCOMP | 9P 2 | Cumulative quantity reported complete. |
| WOSCRAP | 9P 2 | Quantity scrapped. |
| DUEDT / RELDT | 8S 0 | Due date / release date (YYYYMMDD). |
| WOSTAT | 1A | P planned, R released, W in-work, C complete, X cancelled. |
| WOCOST | 11P 2 | Order cost (set by WIP valuation). |
| WORCVD | 9P 2 | Quantity already received — the receipt high-water mark (increment guard). |
FGWOLF is a logical file over FGWO keyed on WOSTAT then
WONO — the status access path for a posting run to read only released/in-work orders.
| Field | Type | Meaning |
|---|---|---|
| WONO / MSEQ | 8A / 3S 0 | Work order + line sequence (key). |
| COMPNO | 8A | Component required. |
| REQQTY | 9P 4 | Required quantity (explosion result, scrap-uplifted). |
| ISSQTY | 9P 4 | Quantity actually issued to the shop floor. |
| MSTAT | 1A | O open, I issued. |
| Field | Type | Meaning |
|---|---|---|
| TRNSEQ | 8S 0 | Ledger sequence (key) — derived from a stable business key (see F.3). |
| ITEMNO | 8A | Item affected. |
| TTYPE | 1A | I issue, R receipt, A count adjustment, V revalue. |
| TDT | 8S 0 | Transaction date (YYYYMMDD, compiled constant per program). |
| TQTY | 9P 2 | Signed quantity (negative for issues). |
| TCOST | 11P 2 | Extended cost (negative for issues). |
| TREF | 8A | Source reference (work-order number, PHYSINV). |
| TMEMO | 25A | Free-text detail. |
The disjoint key ranges (F.3): issues 10000000+, receipts 20000000+, adjustments 40000000+, revalues 60000000+ — so keys from different programs never collide and every one is regenerable from its source event.
| Field | Type | Meaning |
|---|---|---|
| RUNNO | 6S 0 | Regeneration run number (key 1) — successive runs accumulate. |
| ITEMNO | 8A | Item planned (key 2). |
| GROSSRQ | 9P 2 | Gross requirement from open WO demand. |
| NETREQ | 9P 2 | Net requirement (gross less available). |
| SUGQTY | 9P 2 | Suggested replenishment quantity. |
| SUGDT | 8S 0 | Suggested order date. |
| MSGCD | 2A | Message code: OR order, SS below safety stock, blank none. |
| Field | Type | Meaning |
|---|---|---|
| CSEQ | 8S 0 | History sequence (key) — 60000000 + item sequence. |
| ITEMNO | 8A | Item re-costed. |
| OLDCOST / NEWCOST | 11P 4 | Standard cost before / after the roll-up. |
| CDT | 8S 0 | Change date. |
| CREASON | 1A | Reason (R = roll-up). |
| Field | Type | Meaning |
|---|---|---|
| WCNO | 4A | Work-centre number (key). |
| WCDESC | 25A | Description. |
| CAPHRS | 7P 2 | Capacity hours. |
| WCSTAT | 1A | Status. |
| Field | Type | Meaning |
|---|---|---|
| GLSEQ | DECIMAL(8,0) | GL sequence (PK) — continued from MAX() each run (see F.5). |
| GLBATCH | DECIMAL(6,0) | Batch number (YYYYMM). |
| ACCT | CHAR(9) | Account: 1310-RAW, 1320-WIP, 1330-FG. |
| DRCR | CHAR(1) | D debit, C credit. |
| AMT | DECIMAL(11,2) | Posting amount. |
| GLREF | CHAR(8) | Reference (ISSUE / RECEIPT). |
| GLDT | DECIMAL(8,0) | Posting date. |
Index FGGLFACC on (ACCT, DRCR) supports the by-account roll-back the
post reads before continuing.
The reference load writes 7 items (2 finished, 1 sub-assembly, 4 raw), 8 BOM lines and 2 work centres:
| Item | Type | Std cost | On-hand | Safety | Notes |
|---|---|---|---|---|---|
| FG100001 | F | 0 (rolled) | 12 | 5 | Pump assembly 2in. |
| FG100002 | F | 0 (rolled) | 3 | 4 | Pump assembly 4in. |
| SA200001 | S | 0 (rolled) | 20 | 10 | Impeller sub-assembly. |
| RM300001 | R | 42.5000 | 100 | 25 | Cast-iron housing. |
| RM300002 | R | 18.7500 | 60 | 20 | Stainless shaft. |
| RM300003 | R | 25.0000 | 40 | 15 | Bronze impeller. |
| RM300004 | R | 6.2500 | 8 | 30 | Seal kit (below safety → the MRP shortage). |
BOM: FG100001 → RM300001×1, RM300002×1 (2% scrap),
SA200001×1, RM300004×2; SA200001 → RM300003×1, RM300004×1;
FG100002 → RM300001×2, RM300002×1.
CALL FORGE/FGSETUP, then load reference
data with CALL FORGE/FGREFLD.FORGE (LIBL = QSYS QGPL FORGE QTEMP).FGWO (WOSTAT='P').SBMJOB CMD(CALL PGM(FORGE/FGDAILY)).CALL FORGE/FGMENU
(option 1 item inquiry, option 2 work-order subfile).Post-checks after the daily cycle:
FGWOREL RELEASED= equals the planned orders you entered; each produced one
FGWOMAT line per active BOM line, and the components' ALLOC rose (on-hand
unchanged).FGISSUE ISSUED= equals the open material lines; each drew stock (on-hand and allocation
both fell) and wrote one negative I ledger row.FGRECPT RECEIVED= equals the orders with new completions; finished on-hand rose by the
increment since the last receipt, and a fully-made order rolled to WOSTAT='C'.Weekly MRP run:
FGWEEK. Confirm FGMRPGEN RUN=202631 PLANNED= equals the active-item
count and FGMRPPR LINES= equals the number of suggested orders.SELECT ITEMNO, GROSSRQ, NETREQ, SUGQTY, MSGCD FROM FORGE/FGMRP WHERE RUNNO = 202631 AND SUGQTY > 0;
Month-end close:
FGMONTH. Confirm the FGCSTRL COSTED=, FGWIPVL WIP=,
FGGLPST ROWS= and the COBOL FGGLRPT totals all appear, ending with the
month-close banner.Reconciling figures (the same ones the cycles simulation checks against a hand-derived oracle):
QTYPER × n × (1 + SCRAPPC/100); e.g. 10×FG100001 needs shaft 10.2000.SA200001 = 31.2500; FG100001 = 105.3750;
FG100002 = 103.7500 (F.2). Each change leaves an FGCSTH before/after row.FGITRN across all months posted; and SUM(DR) = SUM(CR):SELECT SUM(AMT) FROM FORGE.FGGLFEED WHERE DRCR = 'D';
SELECT SUM(AMT) FROM FORGE.FGGLFEED WHERE DRCR = 'C'; -- must match, non-zero
SELECT ACCT, DRCR, SUM(AMT) FROM FORGE.FGGLFEED GROUP BY ACCT, DRCR;
| Situation | Behaviour | Action |
|---|---|---|
| Re-run the daily cycle | Each posting program's stable ledger key is already present. | Safe no-op: ISSUED=0, and on-hand does not move again. Idempotent. |
| Re-run FGWOREL | Orders already past planned are skipped. | No double-allocation; material lines are not duplicated. |
| Re-run FGMRPGEN same week | (RUNNO, ITEMNO) already exists. | PLANNED=0; one row per item remains. Idempotent per week. |
| Re-run FGPHYS | The item's count adjustment key is already posted. | ADJUSTED=0. Idempotent. |
| Re-run FGMONTH (new activity since last close) | GLSEQ continues from MAX(); only new movement posts. | The GL is not frozen — the second month's activity posts and DR still equals the issue ledger (F.5). |
| Partial completion reported, then the rest | Each receipt posts only WOCOMP − WORCVD under a per-receipt key. | Report increments across days; on-hand credits each increment exactly once (F.5). |
FGITRN with a signed quantity and
extended cost, and every cost change to FGCSTH with a before image, any cycle's effect is
fully reconstructable after the fact for reconciliation and recovery. The cycles test verifies the ledger
accumulates every transaction class (I/R/A) and that every ledger key is unique.The complete program surface, from sources.mjs. All objects are in library
FORGE. RPG is written column-exact fixed-form C/D specs with /free blocks;
the helpers C(...) and D(...) at the top of the source emit the exact column
positions.
write-ing hard-coded records; DSPLYs a load summary.R. Skips non-planned orders.I ledger row, sets MSTAT=I.WOCOMP−WORCVD) under a per-receipt-event key (WO×100 + slot), adds to ONHAND at standard, advances WORCVD, rolls WOSTAT to C or W.KLIST of both key fields drives the idempotency CHAIN.STDCOST×QTYPER (scrap-uplifted) one BOM level down, updates STDCOST if changed, writes an FGCSTH before/after row.ISSQTY×STDCOST over its material lines into WOCOST.EXEC SQL INSERT, continuing GLSEQ from MAX() and posting only the movement since the last close (F.5).SUGQTY>0) writes a detail line to the FGMRPP printer file, handling page overflow (OFLIND(*IN90)).A variance adjustment (deterministic −2 count) under an item-sequence key and corrects ONHAND.Explosion (FGWOREL) walks FGBOM for the WO's parent, one level only, and for each active line
computes reqqty = qtyper × woqty, then if scrappc > 0 uplifts by
reqqty × scrappc / 100. A sub-assembly appears as a material line on the
parent's order — the explosion is deliberately single-level; the sub-assembly's own components are
planned/made through its own work order.
Roll-up (FGCSTRL) is the bottom-up complement, in two passes so a finished good picks up its sub-assembly's freshly-computed cost:
SA200001 = RM300003(25.0000)x1 + RM300004(6.2500)x1 = 31.2500
FG100001 = RM300001(42.5000)x1
+ RM300002(18.7500)x1 @2% scrap = 19.1250
+ SA200001(31.2500)x1
+ RM300004(6.2500)x2 = 12.5000 = 105.3750
FG100002 = RM300001(42.5000)x2 = 85.0000 + RM300002(18.7500)x1 = 103.7500
The four-decimal STDCOST (11P4) is what keeps the scrap-uplifted shaft cost
(19.1250) exact through the roll-up rather than losing cents at each level.
FORGE/i has no LOANSVC-style control row. Instead every posting program derives its FGITRN key from a stable business key, so a re-run regenerates the same key and the CHAIN guard refuses a duplicate. The ranges are disjoint so keys from different programs never collide:
| Program | Type | Key formula |
|---|---|---|
| FGISSUE | I | 10000000 + WOdigits×100 + line |
| FGRECPT | R | 20000000 + WOdigits×100 + receipt-slot |
| FGPHYS | A | 40000000 + item-sequence |
| FGCSTRL | (FGCSTH) | 60000000 + item-sequence (cost-history key CSEQ) |
The guard idiom is CHAIN(EN) on the derived key with an error/found indicator, then skip
if present. FGMRPGEN uses the same discipline in FGMRP but over a composite key
(RUNNO, ITEMNO) via a KLIST — the source notes that chaining on RUNNO alone would match
the first row of the run and wrongly skip every later item.
SQL is confined to the GL. FGGLFEED is created by RUNSQLSTM (COMMIT(*NONE))
from the FGGLFD/FGGLFC members. FGGLPST reads the ledger with
native RPG I/O but posts with embedded SQL:
select coalesce(max(GLSEQ),0) into :wseq from FORGE.FGGLFEED;exec sql insert into FORGE.FGGLFEED values (:wseq, :wbatch, :wacct, :wdc, :wamt, :wref, 20260831);The postings are matched pairs: on issues, credit 1310-RAW / debit 1320-WIP;
on receipts, credit 1320-WIP / debit 1330-FG — so WIP is posted on both
sides and the batch balances.
Both are documented as fixes directly in the source, and both are regression-guarded by
test/forge_daily.mjs / forge_cycles.mjs:
WOCOMP is cumulative. The original per-WO ledger key meant the first receipt
permanently blocked every later one: a shop floor reporting 4 today and 6 more tomorrow silently
lost the 6 — never added to on-hand, never ledgered. The key is now per receipt event
(WO×100 + receipt slot) and each posting credits only WOCOMP−WORCVD, so
every increment posts exactly once. Test: 6 then 4 takes on-hand 18→22 with two ledger rows.GLSEQ at 1 every run and re-totalled from scratch, so
month 2 collided with month 1 on FGGLFEED's primary key. The inserts were correctly
refused — but SQLCOD was never checked, so the job reported success while the GL
silently stopped tracking the inventory ledger, permanently. The fix continues GLSEQ
from MAX(), posts only the movement since the last close (net of the per-account
running totals), and counts a row only when SQLCOD = 0. Test: a second month-end posts
the new activity and the GL debit total still equals the issue ledger exactly.SBMJOB CMD(CALL PGM(FORGE/FGDAILY)).reqqty × (1 + SCRAPPC/100).EXEC SQL INSERT into the SQL GL feed.