VINTAGE/i — Winery Production, Cellar Inventory & Distribution/Sales

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

VINTAGE/i runs a small winery's own operation end to end: harvest lots brought in from vineyard blocks, fermentation batches blended from those lots, movements of wine through the cellar's tanks and barrels, bottling runs into finished-goods SKUs, sales orders and invoicing into accounts receivable, a standard-cost roll-up from grape cost to bottle cost, and a monthly GL post and close. The logic is classic IBM i: fixed-form and free-form RPG/ILE programs over keyed DB2 for i physical and logical files, two 5250 inquiry screens (one with a subfile), an ILE COBOL trial-balance report, an SQLRPGLE GL-feed poster, and CL job cycles that chain the batch programs. This manual is the reference for the operator who runs the online screens and the daily/monthly cycles, and for the developer maintaining the application. It is grounded entirely in the committed source (winery-app/src/sources.mjs, src/seed.mjs, and the four test/vintage_*.mjs battle drivers). Everything runs in library VINTAGE.

Contents

A. Overview & Architecture ↑ top

A.1 What it does

VINTAGE/i covers the full life of a bottle of wine, from fruit on the crush pad to a receivable on the books:

A.2 The production & cost model: standard cost that conserves

Two properties are designed in and verified by the test battles, and both matter operationally:

Every posting program is idempotent: durable ledgers (VNCMOV, VNAR, VNCSTH) are keyed on a sequence derived from a stable business key (the batch, run, order or SKU number), so a re-run regenerates the same key and the CHAIN(EN) existence check refuses to double-post. State-flag guards (BSTAT, RSTAT, OSTAT, LSTAT, and BATCOST>0) skip work already done. This is what makes the whole daily and monthly chains safely re-runnable, which section E leans on.

A.3 Component & flow

  PRODUCTION                         DISTRIBUTION            CLOSE (monthly)
  ----------                         ------------            ---------------
  VNBATREL   grape cost -> batch     VNORDREL  allocate      VNCSTRL  cost roll-up
    VNLOT -> VNBATCH (+VNBCOMP)        VNORDL -> VNSKU.ALLOC    runs -> VNSKU.STDCOST
  VNCELLPOST batch -> cellar         VNINVPOST invoice        (+ VNCSTH history)
    VNBATCH -> VNCELL (+VNCMOV 'I')    VNSKU relieved         VNGLPST  GL post
  VNBTLPOST  cellar -> bottles        VNINV + VNAR 'I'          VNCMOV+VNAR -> VNGLFEED
    VNCELL -> VNSKU (+VNCMOV 'O')      + VNCUST.ARBAL         VNARPT   AR trial bal.

  ONLINE (5250)                      LEDGERS (durable, idempotent)
  -------------                      -----------------------------
  VNMENU                             VNCMOV  cellar movements  (I/O/... keyed MOVSEQ)
    1 -> VNCELLIQ  (plain)           VNAR    receivables       (I/P    keyed ARSEQ)
    2 -> VNMOVIQ   (subfile)         VNCSTH  cost history      (       keyed CSEQ)
                                     VNGLFEED SQL GL feed      (DR/CR  keyed GLSEQ)

  CYCLES:  VNDAILY = VNBATREL -> VNCELLPOST -> VNBTLPOST -> VNORDREL -> VNINVPOST
           VNMONTH = VNCSTRL  -> VNGLPST    -> VNARRPT

A single production event flows left to right: the harness (or an order-entry front end) seeds the planning rows — a batch header and its blend recipe, a bottling run, a sales order — and the daily cycle posts them, each program advancing one stage, updating masters and writing one durable ledger row per event. The monthly cycle then values and closes what the month produced.

A.4 Object inventory

ObjectTypeRole
VNVYDPFVineyard-block master.
VNLOTPFHarvest / grape lot (priced per tonne).
VNLOTLFLFLots by vineyard (where-grown access path).
VNBATCHPFFermentation batch header.
VNBCOMPPFBatch composition (which lots, how many tons).
VNCELLPFCellar location master (tank/barrel, book inventory).
VNCMOVPFCellar-movements ledger (durable audit trail).
VNCMOVLFLFMovements by vessel (enquiry access path).
VNSKUPFFinished-goods SKU master (on-hand, alloc, std cost).
VNBTLPFBottling-run header.
VNCUSTPFCustomer master.
VNORD / VNORDLPFSales-order header / lines.
VNINVPFInvoice header (one per order invoiced).
VNARPFAR ledger (durable receivables history).
VNARLFLFAR by customer (statement access path).
VNCSTHPFStandard-cost change history.
VNGLFEEDSQL tableGL feed the monthly close posts into (+ index VNGLFACC).
VNCELLD / VNMOVD / VNMENUDDSPFCellar inquiry / movements subfile / menu display files.
VNARPRTPRTFAR aging / trial-balance printer file.
VNREFLDRPGLESeed reference data (vineyards, lots, cellars, SKUs, customers).
VNBATRELRPGLEGrape-cost roll-up onto the batch.
VNCELLPOSTRPGLECellar a fermented batch (daily).
VNBTLPOSTRPGLEPost bottling runs (daily).
VNORDRELRPGLEAllocate sales-order lines (daily).
VNINVPOSTRPGLEInvoice orders, post AR (daily).
VNCSTRLRPGLEStandard-cost roll-up (monthly).
VNGLPSTSQLRPGLEGL post of cellar + AR movement (monthly).
VNCELLIQ / VNMOVIQ / VNMENURPGLECellar inquiry / movements subfile / menu.
VNARRPTCBLLEILE COBOL AR trial-balance report (monthly).
VNSETUP / VNDAILY / VNMONTHCLPBuild CL + the two job cycles.

The catalogue is 14 physical files, 3 logical files, 1 SQL table + 1 SQL index, 3 display files and 1 printer file, driven by 11 RPG programs, 1 ILE COBOL program and 3 CL programs. Sections D and F expand each.

B. Online Transactions & Screens ↑ top

B.1 The command/entry line

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

To do thisType on the command line
Open the operator main menuCALL VINTAGE/VNMENU
Open the cellar inquiry screen directlyCALL VINTAGE/VNCELLIQ
Open the cellar-movements subfile screen directlyCALL VINTAGE/VNMOVIQ
Run the daily production/distribution cycleCALL VINTAGE/VNDAILY (or SBMJOB it)
Run the monthly cost roll-up & closeCALL VINTAGE/VNMONTH
Build/rebuild every object & programCALL VINTAGE/VNSETUP
Load the reference (master) dataCALL VINTAGE/VNREFLD

The batch drivers take no CALL parameters. They are date-independent postings: each program scans its driving file (batches, runs, orders, SKUs) and posts every row that is in a state ready to advance, so a scheduled submission is a bare CALL. Only VNMENU, VNCELLIQ and VNMOVIQ are interactive; the batch programs run to completion and DSPLY a one-line POSTED=/SKIP= result.

B.2 The menu & the two inquiry screens

VINTAGE/i's interactive surface is a menu that routes by program-to-program CALL to two read-only inquiry screens. There is no online data-entry or maintenance screen — masters and orders are created by the seed program / harness (or an upstream order-entry front end) and advanced by the batch posters; the 5250 screens are for looking, not editing.

VNMENU / VNMENUD — the main menu

A plain menu record. The operator keys an option and Enter; option 1 issues CALL 'VNCELLIQ', option 2 issues CALL 'VNMOVIQ', any other non-blank option redisplays with Invalid option. in the message line. F3 ends the program.

VINTAGE/i Main Menu 1. Cellar Inquiry 2. Cellar Movements Option . . . . : _ F3=Exit Enter=Select

VNCELLIQ / VNCELLD — cellar inquiry (plain screen)

The operator keys a cellar/tank ID and Enter. VNCELLIQ CHAINs VNCELL and, on a hit, renders the vessel type, capacity, current gallons, contents variety, source batch, and a derived fill percentage computed as (CURGAL × 100) / CAPGAL. A miss clears the detail and shows Cellar location not found. — never stale values.

Cellar Inquiry - VINTAGE/i Cellar/tank ID: CEL001 Type . . . . . : T Capacity (gal) : 2,500.00 Current (gal) : 1,750.00 Fill percent . : 70.00 Contents . . . : CABERNET From batch . . : BA900001 Cellar location found. F3=Exit Enter=Inquire

VNMOVIQ / VNMOVD — cellar-movements inquiry (subfile screen)

The app's one subfile screen (DDS record MSFL under control record MCTL, SFLPAG(5) per page, SFLSIZ(20), ROLLUP/ROLLDOWN paging, SFLEND(*MORE)). The operator keys a cellar/tank ID; VNMOVIQ shows the vessel's current gallons in the header, then loads every movement for that vessel — in sequence order via the VNCMOVLF access path — into the subfile. It uses the classic load-then-display discipline: clear (SFLCLR) and reload happen after the EXFMT that read the operator's key, so a fresh enquiry genuinely clears the previous vessel's rows (an empty vessel shows zero rows). Negative (bottling-draw) gallons render with a trailing minus, e.g. 500.00-.

Cellar Movements - VINTAGE/i Cellar/tank ID: CEL001 Current (gal) : 1,750.00 Seq Ty Gallons Cost Reference 10000001 I 2,250.00 18,225.00 BA900001 30900001 O 500.00- 4,050.00- BT900001 Cellar location found. F3=Exit Roll=Page Enter=Inquire

Subfile columns (MSFL)

FieldType (DDS)Shows
MSEQD10Y,0 outputMovement sequence (from MOVSEQ).
MTYPD1A outputMovement type: I transfer-in, O transfer-out (draw).
MGALD12A outputGallons (edited; negative on a draw).
MCSTD12A outputCost (edited; negative on a draw).
MREFD8A outputReference (source batch or run number).

Actions & keys

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

Honest statement: VINTAGE/i does not model a true four-eyes maker–checker / separate-authorization workflow, and it has no interactive posting at all — the 5250 screens are read-only inquiries. Postings are made by the unattended batch programs; there is no "one user posts, a second user approves" gate anywhere in the code. The manual documents the control model the application does have, all enforced in the data/posting layer:

In sum, the control posture is durable ledgers + idempotency + state gating + GL balancing, enforced by the posting programs, rather than a segregation-of-duties approval workflow.

C. Batch Jobs & the Periodic Cycle ↑ top

VINTAGE/i runs as two job cycles, not a monolithic nightly job: a daily cycle that posts production and distribution, and a monthly cycle that rolls up cost and closes. There is deliberately no weekly cycle — a winery's cadence is harvest-season driven, not a weekly MRP regen, so the weekly-shaped work (invoicing) sits inside the daily distribution chain, matching real small-winery practice. Both cycles are CL programs that simply CALL their component programs in order; all the batch programs are parameterless.

-- run the whole daily production + distribution cycle
SBMJOB CMD(CALL PGM(VINTAGE/VNDAILY)) JOB(VNDAILY)
-- run the monthly cost roll-up + GL close
SBMJOB CMD(CALL PGM(VINTAGE/VNMONTH)) JOB(VNMONTH)

The two CL cycles are, verbatim in intent from the source:

VNDAILY:  CALL VNBATREL  -> CALL VNCELLPOST -> CALL VNBTLPOST
           -> CALL VNORDREL -> CALL VNINVPOST
VNMONTH:  CALL VNCSTRL   -> CALL VNGLPST    -> CALL VNARRPT

C.1 Full batch program set

ProgramPurposeReads / writesOutputs (DSPLY + effect)Cycle
VNBATREL Roll grape cost from a batch's lot lines onto the batch; allocate lot tonnage. Reads VNBCOMP; updates VNLOT (TONALLOC/LSTAT), VNBATCH (BATCOST/GALACT). VNBATREL POSTED=n SKIP=n; BATCOST=Σ(tons×toncost), GALACT=tons×150. Daily
VNCELLPOST Transfer a fermented batch into its target cellar vessel; price the vessel per gallon. Updates VNCELL (CURGAL/CSTGAL/contents), VNBATCH (→'C'); writes VNCMOV 'I'. VNCELLPOST POSTED=n SKIP=n; CSTGAL=BATCOST/gallons. Daily
VNBTLPOST Draw gallons out of a vessel, convert to bottles, add to a SKU; complete the run. Updates VNCELL (CURGAL−), VNSKU (ONHAND+), VNBTL (→'C', BTLQTY/RUNCOST); writes VNCMOV 'O'. VNBTLPOST POSTED=n SKIP=n; bottles=gal/0.2, RUNCOST=gal×CSTGAL. Daily
VNORDREL Allocate open order lines' bottles against SKU on-hand. Reads VNORDL; updates VNSKU (ALLOC+). VNORDREL ALLOC=n SKIP=n; ALLOC += cases×12. Daily
VNINVPOST Invoice open orders: relieve inventory, cut the invoice, post AR debit. Updates VNORDL/VNORD (→'I'), VNSKU (ONHAND−/ALLOC−), VNCUST (ARBAL+); writes VNINV, VNAR 'I'. VNINVPOST INVOICED=n SKIP=n; INVAMT=Σ(bottles×uprice). Daily
VNCSTRL Roll each SKU's standard bottle cost up from its completed bottling runs. Reads VNBTL; updates VNSKU (STDCOST); writes VNCSTH. VNCSTRL COSTED=n; STDCOST=ΣRUNCOST/ΣBTLQTY. Monthly
VNGLPST Sum the month's cellar & AR movement, post balanced DR/CR pairs to the GL feed. Reads VNCMOV, VNAR; inserts into SQL VNGLFEED under commitment. VNGLPST BATCH=202610 ROWS=n; 3 balanced pairs = 6 rows on a first close. Monthly
VNARRPT ILE COBOL AR trial-balance report (invoiced vs. payments vs. txn count). Reads VNAR sequentially. VNARRPT INVOICED /PAYMENTS /TXNS lines. Monthly

C.2 Daily / monthly detail

Daily — VNDAILY

The daily cycle walks one wine cohort from batch to receivable. Using the seeded scenario the test battle asserts against (two batches, two bottling runs, two orders):

Expected DSPLY (the seeded two-cohort daily run):
  VNBATREL POSTED=2 SKIP=0
  VNCELLPOST POSTED=2 SKIP=0
  VNBTLPOST POSTED=2 SKIP=0
  VNORDREL ALLOC=2 SKIP=0
  VNINVPOST INVOICED=2 SKIP=0
A second run of the same cycle over the same data DSPLYs all zeros for the posted counts (everything is either state-advanced or ledger-guarded), so the daily cycle is safely re-runnable — the battle asserts CEL001 gallons and each customer's AR balance do not move on a re-run.

Monthly — VNMONTH

After a month of dailies, the monthly cycle values and closes:

Expected DSPLY (monthly close of the seeded month):
  VNCSTRL COSTED=2
  VNGLPST BATCH=202610 ROWS=6
  VNARRPT INVOICED  20880.00
  VNARRPT PAYMENTS       0.00
  VNARRPT TXNS           2
The monthly cycle is also safely re-runnable: VNCSTRL writes no new cost-history when nothing changed, and VNGLPST posts ROWS=0 because the movement since the last close is zero — the feed stays at 6 rows.

C.3 Ordering & dependencies

D. Data Files (Data Dictionary) ↑ top

Every file is a DDS-described DB2 for i object in library VINTAGE, created by VNSETUP from source members in VINTAGE/QDDSSRC (the SQL GL feed from QSQLSRC). Packed decimals carrying money are held to enough precision that the standard-cost roll-up rounds cleanly: per-tonne grape cost and per-gallon / per-bottle cost are 11P 4 (four decimals), while ledger amounts and balances are 11P 2. Field lengths below are the DDS declarations verbatim (nA=character, nP d=packed with d decimals, nS d=zoned/signed with d decimals). Keys are marked K.

D.1 Reference & production masters

VNVYD — vineyard-block master (UNIQUE, key VYDID)

FieldTypeMeaning
VYDID6A KVineyard block / parcel ID (VYD001).
VYDNAME25ABlock name (RIDGE BLOCK).
VARIETY15AGrape variety grown in the block.
ACRES6P 2Planted acres.
VSTAT1AStatus (A=active).

VNLOT — harvest / grape lot (UNIQUE, key LOTID)

Tonnage brought in from a vineyard block, priced per tonne. TONCOST is 11P 4 so a fractional-cent per-tonne price still rolls up cleanly through fermentation and bottling. The lot is the entry point of grape cost into the whole chain.

FieldTypeMeaning
LOTID8A KHarvest-lot ID (LOT00001).
VYDID6ASource vineyard block.
HVDT8S 0Harvest date (CCYYMMDD).
VARIETY15AGrape variety.
TONS9P 2Tonnage received.
TONCOST11P 4Cost per tonne (grape cost enters here).
TONALLOC9P 2Tonnage allocated to batches (bumped by VNBATREL).
LSTAT1AStatus: O=open (still assignable), A=assigned (fully allocated), C=closed.

VNLOTLF — lot access path by vineyard (LF over VNLOT)

Non-master logical file keyed VYDID, LOTID — a where-grown enquiry path (all lots from one block). The base VNLOT's own key is LOTID only.

VNBATCH — fermentation batch header (UNIQUE, key BATCHNO)

One batch is one tank's worth of must/wine through fermentation. BATCOST accumulates grape cost as lots are assigned (the standard-cost roll-up's starting point); GALACT is set by VNBATREL from tonnage at the fixed 150 gal/ton yield.

FieldTypeMeaning
BATCHNO8A KBatch number (2 letters + 6 digits, BA000001).
VARIETY15AWine variety.
VINTAGE4S 0Vintage year.
STARTDT8S 0Fermentation start date.
GALEXP9P 2Expected gallons (planning figure).
GALACT9P 2Actual gallons = tons × 150 (set by VNBATREL).
TGTCELL6ATarget cellar vessel this batch will be transferred into.
BSTAT1AStatus: F=fermenting, C=cellared, X=cancelled.
BATCOST11P 4Rolled-up grape cost (Σ tons×toncost).

VNBCOMP — batch composition / blend recipe (UNIQUE, key BATCHNO+BSEQ)

Which lots, and how many tons of each, went into a batch. A batch can draw multiple lots (a blend at the must stage); a lot can feed multiple batches (split across tanks). VNBATREL reads these lines to roll grape cost onto the batch and allocate the lots.

FieldTypeMeaning
BATCHNO8A KBatch this line belongs to.
BSEQ3S 0 KComposition line sequence within the batch.
LOTID8AHarvest lot drawn.
TONSUSED9P 2Tons of that lot used in this batch.

VNCELL — cellar location master (UNIQUE, key CELLID)

A tank or barrel. CURGAL is the book inventory the movements ledger keeps in step with; CSTGAL is the vessel's cost per gallon, set when a batch is cellared into it.

FieldTypeMeaning
CELLID6A KVessel ID (CEL001).
CTYPE1AVessel type: T=tank, B=barrel.
CAPGAL9P 2Capacity in gallons.
CURGAL9P 2Current gallons held (book inventory).
CSTGAL11P 4Cost per gallon = BATCOST / gallons cellared.
CONTVAR15AContents variety.
CONTBAT8ASource batch that filled the vessel.
CSTAT1AStatus (A=active).

VNSKU — finished-goods SKU master (UNIQUE, key SKUID)

One bottled product. STDCOST is the rolled-up per-bottle standard cost (11P 4); ONHAND and ALLOC are in bottles, not cases.

FieldTypeMeaning
SKUID8A KSKU ID (SKU00001).
SKUDESC30ADescription (CABERNET SAUVIGNON 750ML).
VARIETY15AVariety.
VINTAGE4S 0Vintage year.
BTLSIZE4P 0Bottle size in ml (750).
STDCOST11P 4Standard per-bottle cost (set by VNCSTRL).
PRICE11P 2List price per bottle.
ONHAND9P 2On-hand bottles.
ALLOC9P 2Allocated bottles (reserved by open orders).
SSTAT1AStatus (A=active).

VNBTL — bottling-run header (UNIQUE, key RUNNO)

Takes gallons out of a cellar vessel and produces bottles of a SKU at the fixed 0.2 gal/bottle rate. RUNCOST carries the vessel's cost/gallon out into the run — the middle link of the cost chain, which VNCSTRL later divides by BTLQTY to price the bottle.

FieldTypeMeaning
RUNNO8A KRun number (BT000001).
CELLID6ASource vessel drawn from.
SKUID8ATarget finished-goods SKU.
BTLDT8S 0Bottling date.
GALUSED9P 2Gallons drawn from the vessel.
BTLQTY9P 0Bottles produced = GALUSED / 0.2 (set by VNBTLPOST).
RSTAT1AStatus: P=planned, C=complete.
RUNCOST11P 2Run cost = GALUSED × vessel CSTGAL.

D.2 Cellar & receivables ledgers, sales files

VNCMOV — cellar-movements ledger (UNIQUE, key MOVSEQ)

The durable audit trail for wine in the cellar. Every posting program contributes to one accumulating history keyed on MOVSEQ, whose value is derived from a stable business key so a re-run regenerates the same key and is refused rather than double-posted. A transfer-in (I) carries a positive gallon/cost; a bottling draw (O) carries a negative gallon/cost.

FieldTypeMeaning
MOVSEQ8S 0 KMovement sequence (10000000+batch for xfer-in, 30000000+run for draw).
CELLID6AVessel this movement is against.
MTYPE1AType: I=transfer-in, O=transfer-out (draw).
MDT8S 0Movement date.
MGAL9P 2Gallons (negative on a draw).
MCOST11P 2Cost (negative on a draw).
MREF8AReference: source batch (I) or run (O).
MMEMO25AFree-text memo (BATCH TRANSFER TO CELLAR / BOTTLING DRAW).

VNCMOVLF — movements access path by vessel (LF over VNCMOV)

Non-unique logical file keyed CELLID, MOVSEQ. The base PF's MOVSEQ-only key has no relationship to CELLID, so the movements-inquiry screen (VNMOVIQ) reads this path to browse one vessel's history in sequence order.

VNCUST — customer master (UNIQUE, key CUSTID)

FieldTypeMeaning
CUSTID6A KCustomer ID (CUS001).
CUSTNAME30ACustomer name.
TERMS2APayment terms in days (30).
CRLIMIT11P 2Credit limit.
ARBAL11P 2Outstanding AR balance (bumped by VNINVPOST).
CSTAT1AStatus (A=active).

VNORD / VNORDL — sales-order header & lines

VNORD is UNIQUE keyed ORDNO; VNORDL is UNIQUE keyed ORDNO, OSEQ. A case is 12 bottles (fixed, matching the bottling conversion).

File.FieldTypeMeaning
VNORD.ORDNO8A KOrder number (OR000001).
VNORD.CUSTID6AOrdering customer.
VNORD.ORDDT8S 0Order date.
VNORD.OSTAT1AStatus: O=open, I=invoiced, X=cancelled.
VNORD.ORDTOT11P 2Order total (set at invoice).
VNORDL.ORDNO8A KOwning order.
VNORDL.OSEQ3S 0 KLine sequence.
VNORDL.SKUID8ASKU ordered.
VNORDL.CASES7P 2Cases ordered (×12 = bottles).
VNORDL.UPRICE11P 2Unit price per bottle.
VNORDL.LSTAT1AStatus: O=open, I=invoiced.

VNINV — invoice header (UNIQUE, key INVNO)

One row per order invoiced. Note the deliberately distinct field names IORDNO and ICUSTID (not the shared ORDNO/CUSTID) — the structural fix for this emulator's flat-symbol-table hazard, detailed in F.3.

FieldTypeMeaning
INVNO8A KInvoice number (= order number in this app).
IORDNO8AOrder invoiced (distinct name, see F.3).
ICUSTID6ACustomer billed (distinct name, see F.3).
INVDT8S 0Invoice date.
INVAMT11P 2Extended total = Σ(bottles × UPRICE).
INVCOST11P 2Extended standard cost = Σ(bottles × STDCOST).

VNAR — AR ledger (UNIQUE, key ARSEQ) & VNARLF (LF)

Durable receivables history. ARSEQ = 50000000+order for an invoice, so a re-run regenerates the same key and the CHAIN(EN) guard refuses the second write. ACUSTID is a distinct name (not CUSTID) for the same F.3 reason. VNARLF is a logical file keyed ACUSTID, ARSEQ — the customer-statement access path.

FieldTypeMeaning
ARSEQ8S 0 KAR sequence (50000000+order digits).
ACUSTID6ACustomer (distinct name, see F.3).
ATYPE1AType: I=invoice (debit), P=cash payment (credit).
ADT8S 0Posting date.
AAMT11P 2Amount.
AREF8AReference (order number).

VNCSTH — standard-cost change history (UNIQUE, key CSEQ)

Every standard-cost change on a SKU, with a before/after image, so the monthly close has an audit trail. CSEQ = 60000000+SKU.

FieldTypeMeaning
CSEQ8S 0 KCost-history sequence (60000000+SKU digits).
SKUID8ASKU whose cost changed.
OLDCOST11P 4Cost before the change.
NEWCOST11P 4Cost after the change.
CDT8S 0Change date.
CREASON1AReason code (R=roll-up).

D.3 The GL feed (SQL) & the fixed conversions

VNGLFEED — SQL GL feed (PRIMARY KEY GLSEQ) + index VNGLFACC

An SQL table (not a DDS PF), created by RUNSQLSTM over QSQLSRC/VNGLFD, that the monthly close (VNGLPST) inserts balanced DR/CR pairs into under commitment control. Index VNGLFACC is on (ACCT, DRCR) — the access path VNGLPST's “already-posted” sub-selects use.

ColumnSQL typeMeaning
GLSEQDECIMAL(8,0) PKFeed sequence, continued from the prior close's max.
GLBATCHDECIMAL(6,0)Close batch = period (202610).
ACCTCHAR(9)GL account (see the account map in F.2).
DRCRCHAR(1)D=debit, C=credit.
AMTDECIMAL(11,2)Posted amount.
GLREFCHAR(8)Reference (CELRIN / BOTTLE / ARINV).
GLDTDECIMAL(8,0)Post date (20261031).

Fixed conversions (deliberate round numbers, so all arithmetic is exact and testable)

ConversionValueWhere used
Grape yield1 ton → 150.00 gallonsVNBATREL: GALACT = tons × 150.
Bottle fill0.2 gal / bottleVNBTLPOST: bottles = GALUSED / 0.2.
Case size12 bottles / caseVNORDREL / VNINVPOST: bottles = CASES × 12.
Field-length reminder: ONHAND/ALLOC/CURGAL are gallons or bottles as noted, never cases. The one place cases appear is VNORDL.CASES; everything downstream is bottles. This is why a 50-case order shows as 600 bottles allocated and relieved.

E. Operations Runbook ↑ top

This section is the operator's procedural guide: what to run when, what a good run looks like, and what to do when one fails. It uses the seeded two-cohort scenario the test battles assert against, so every figure here is a number you can reconcile against a real run.

E.1 Day-in-the-life — the daily production & distribution run

The daily job is VNDAILY, which chains the five daily posters in the fixed order VNBATREL → VNCELLPOST → VNBTLPOST → VNORDREL → VNINVPOST. Run it once per operating day after the day's planning rows (batch headers + blend recipes, bottling runs, sales orders) have been seeded by the harness / order-entry front end.

  1. Confirm the library list. The job must run with VINTAGE on the library list (LIBL = QSYS QGPL VINTAGE QTEMP, CURLIB = VINTAGE). VNDAILY issues its own ADDLIBLE LIB(VINTAGE) as a belt-and-braces step.
  2. Submit the cycle.
    SBMJOB CMD(CALL PGM(VINTAGE/VNDAILY)) JOB(VNDAILY)
    Or CALL VINTAGE/VNDAILY interactively for an attended run.
  3. Read the five DSPLY lines. A clean run over the seeded two-cohort scenario prints:
      VNBATREL POSTED=2 SKIP=0
      VNCELLPOST POSTED=2 SKIP=0
      VNBTLPOST POSTED=2 SKIP=0
      VNORDREL ALLOC=2 SKIP=0
      VNINVPOST INVOICED=2 SKIP=0
    followed by the banner VINTAGE daily production and distribution cycle complete.
  4. Spot-check the masters against the day's expected figures (this is the batch battle's oracle):
    • Batch BA000001: BATCOST=18225.0000, GALACT=2250.00; BA000002: 7200.0000 / 1200.00.
    • Vessel CEL001: CSTGAL=8.1000, drawn down to CURGAL=250.00 after bottling; CEL003: 6.0000, drawn to 0.00.
    • SKU on-hand after invoicing: SKU00001=9400 (10000−600), SKU00002=5640 (6000−360); both ALLOC back to 0.
    • Customer AR: CUS001 ARBAL=14400.00, CUS002 ARBAL=6480.00.
Inventory conservation check (do this daily). Gallons in the cellar are conserved: gallons transferred in (2,250 + 1,200 = 3,450) must equal gallons bottled out (2,000 + 1,200 = 3,200) plus gallons remaining (250.00 in CEL001 + 0.00 in CEL003 = 250). If this identity fails, a movement was posted or mis-posted out of band — investigate VNCMOV before running the monthly close.

E.2 Month-end close — cost roll-up, GL post, trial balance

The monthly job is VNMONTH, chaining VNCSTRL → VNGLPST → VNARRPT. Run it once after the month's dailies have all posted.

  1. Submit the cycle.
    SBMJOB CMD(CALL PGM(VINTAGE/VNMONTH)) JOB(VNMONTH)
  2. Cost roll-up (VNCSTRL). Each SKU's standard bottle cost = its completed runs' cost over their bottles. Expected: SKU00001 = 16200.00 / 10000 = 1.6200, SKU00002 = 7200.00 / 6000 = 1.2000. A cost-history row is written per change (OLDCOST=0 → NEWCOST). DSPLY: VNCSTRL COSTED=2.
  3. GL post (VNGLPST). Three balanced DR/CR pairs (six rows on a first close) land in VNGLFEED under batch 202610. DSPLY: VNGLPST BATCH=202610 ROWS=6.
  4. Trial balance (VNARRPT). The ILE COBOL report sums the AR ledger:
      VNARRPT INVOICED  20880.00
      VNARRPT PAYMENTS       0.00
      VNARRPT TXNS           2

Reconciling figures for the seeded month

These are the numbers a correct close produces, and how they tie together. Every one is asserted by the monthly battle.

GL pairDebit accountCredit accountAmountDerivation
Cellar-in1410-CELR1420-PRAB25,425.00batch costs 18,225 + 7,200
Bottling1430-FG1410-CELR23,400.00run costs 16,200 + 7,200
AR invoices1210-AR4000-SAL20,880.00invoices 14,400 + 6,480
The PAYMENTS line is 0.00 in the seeded scenario because no ATYPE='P' cash credits were posted. When the harness posts payments, they show here as the credit side of the AR trial balance and reduce VNCUST.ARBAL; the GL AR pair still reflects only the invoice movement.

E.3 Failure & re-run rules

Because every posting program is idempotent (durable ledgers keyed on a stable business key + state-flag guards, see A.2 and B.3), the standing rule is simple: if a cycle fails part-way, just run it again. Nothing double-posts.

F. Developer Reference ↑ top

This section is for the developer maintaining VINTAGE/i. Everything below is drawn from the committed source (winery-app/src/sources.mjs), where each program is held as a fixed-form/free RPG, ILE COBOL, CL or SQL member and loaded into VINTAGE's source physical files by src/seed.mjs.

F.1 Program surface

Eleven RPG programs, one ILE COBOL program, three CL programs. The batch posters share one shape: an F-spec block declaring the files, D-spec work fields, a SETLL *LOVAL + READ-loop over the driving file calling a per-row subroutine, and a closing DSPLY of the POSTED=/SKIP= result. All are parameterless.

ProgramTypeFiles open together (F-spec)Driving loop
VNREFLDRPGLEVNVYD, VNLOT, VNCELL, VNSKU, VNCUST (all O)straight-line writes of seed masters.
VNBATRELRPGLEVNBATCH (UF), VNBCOMP (IF), VNLOT (UF)READ VNBATCH → READE VNBCOMP per batch.
VNCELLPOSTRPGLEVNBATCH (UF), VNCELL (UF), VNCMOV (UF A)READ VNBATCH.
VNBTLPOSTRPGLEVNBTL (UF), VNCELL (UF), VNSKU (UF), VNCMOV (UF A)READ VNBTL.
VNORDRELRPGLEVNORDL (UF), VNSKU (UF)READ VNORDL.
VNINVPOSTRPGLEVNORD (UF), VNORDL (UF), VNSKU (UF), VNCUST (UF), VNINV (O A), VNAR (UF A)READ VNORD → READE VNORDL per order.
VNCSTRLRPGLEVNSKU (UF), VNBTL (IF), VNCSTH (UF A)READ VNSKU → nested READ VNBTL per SKU.
VNGLPSTSQLRPGLEVNCMOV (IF), VNAR (IF) + embedded SQL over VNGLFEEDtwo accumulate loops, then SQL inserts.
VNCELLIQRPGLEVNCELLD (CF WORKSTN), VNCELL (IF)EXFMT / CHAIN inquiry loop.
VNMOVIQRPGLEVNMOVD (CF WORKSTN SFILE), VNCELL (IF), VNCMOVLF (IF)load-then-display subfile loop.
VNMENURPGLEVNMENUD (CF WORKSTN)EXFMT / CALL router.
VNARRPTCBLLEAR (VNAR, INDEXED SEQUENTIAL)READ NEXT / EVALUATE AR-TYPE.
VNSETUPCLPDLTF/CRTPF/CRTLF/CRTDSPF/CRTPRTF/RUNSQLSTM/CRTBNDRPG/CRTBNDCBL.
VNDAILY / VNMONTHCLPADDLIBLE + ordered CALLs + banner.

Batch-ID convention: business IDs are 2 letters + 6 digits (BA000001, BT000001, OR000001). The posters extract the numeric part with %dec(%subst(id:3:6):8:0) to build the ledger key.

F.2 Fixed conversions & ledger keys

Two kinds of constants govern the whole application: the physical conversions (D.3) and the ledger-key ranges. The key ranges are disjoint per program and each derived from a stable business key, which is what makes the CHAIN(EN) existence check a correct idempotency guard.

LedgerKey formulaWritten byRange
VNCMOV10000000 + batch digitsVNCELLPOST (transfer-in I)10,000,001…
VNCMOV30000000 + run digitsVNBTLPOST (draw O)30,000,001…
VNAR50000000 + order digitsVNINVPOST (invoice I)50,000,001…
VNCSTH60000000 + SKU digitsVNCSTRL (roll-up)60,000,001…
VNGLFEEDmax(GLSEQ) + 1VNGLPST (running)continued from prior close

The GL account map VNGLPST posts against (hard-coded in the program's glrow calls):

AccountMeaningMovement
1410-CELRCellar WIP inventoryDEBIT on cellar-in, CREDIT on bottling.
1420-PRABGrape / production cost absorbedCREDIT on cellar-in.
1430-FGFinished-goods inventoryDEBIT on bottling.
1210-ARAccounts receivableDEBIT on invoice.
4000-SALSales revenueCREDIT on invoice.
VNGLPST posts the movement since the prior close: for each pair it subtracts what the feed already holds for that account/side (select coalesce(sum(AMT),0) … where ACCT=… and DRCR=…) and posts a pair only if the difference is positive. This is why a second close in the same month posts ROWS=0 and the feed stays balanced.

F.3 The shared-symbol-table hazard (why VNINV/VNAR rename their fields)

This emulator's RPG resolves all unqualified field names through one flat global symbol table shared by every open DISK file in the program. There is no per-file qualification and no PREFIX/RENAME equivalent for F-spec files (unlike the WORKSTN/PRINTER format-conflict check the engine does perform). So if two files open together both declare a field named ORDNO, they are the same variable at runtime, and a CHAIN/READ against one file silently overwrites the value the other file's record just held.

VNINVPOST is where this bites: it has VNORD, VNORDL, VNINV and VNAR all open at once. If VNINV reused ORDNO/CUSTID, then building the invoice header and then CHAINing VNCUST or writing VNAR afterward would clobber the invoice's order/customer with no compile or runtime diagnostic. The structural fix, applied in the DDS, is to give these files distinct field names:

FileRenamed fieldInstead ofWhy
VNINVIORDNOORDNOopen alongside VNORD/VNORDL which own ORDNO.
VNINVICUSTIDCUSTIDopen alongside VNCUST/VNORD which own CUSTID.
VNARACUSTIDCUSTIDopen alongside VNCUST in VNINVPOST.

The program itself stages the volatile values into work fields (WORD, WCUST, WSEQ) up front and writes from those, and it uses two further defensive patterns that recur across the posters:

F.4 Embedded-SQL & screen patterns

VNGLPST — SQLRPGLE over a mixed DDS + SQL surface

The only program that crosses the DDS/SQL boundary. It reads the two DDS ledgers (VNCMOV, VNAR) with native RPG READ loops to accumulate the month's movement, then uses embedded SQL to read and write the VNGLFEED table:

Inquiry screens — CHAIN-and-render, never stale

VNCELLIQ (plain) clears every output field and sets the not-found message before the CHAIN, then overwrites them only on a hit, so a miss never shows a previous vessel's values. The fill percentage is derived, not stored: wpct = (curgal * 100) / capgal, guarded by capgal > 0.

VNMOVIQ — load-then-display subfile discipline

The one subfile program (DDS record MSFL under control MCTL, SFLPAG(5)/SFLSIZ(20)). The critical ordering: the SFLCLR (indicator 31) write and the reload happen after the EXFMT MCTL that reads the operator's key, never before it — clearing ahead of the display would throw away the rows just loaded. Rows are read through the VNCMOVLF access path (keyed CELLID) so one vessel's movements come back in sequence order; an empty vessel genuinely shows zero rows. Numeric subfile fields are edited with %editc(…:'K'), which renders a negative draw with a trailing minus.

VNARRPT — ILE COBOL over the keyed PF side

A hand-written ILE COBOL program that opens VNAR as an INDEXED/ SEQUENTIAL file (record key AR-SEQ), reads it to end, and EVALUATEs AR-TYPE: I adds to the debit (invoiced) accumulator, P to the credit (payments) accumulator. It DISPLAYs three lines (INVOICED, PAYMENTS, TXNS) — the read-only cross-check that the AR ledger sums to what VNINVPOST posted.

G. Glossary ↑ top

Allocation (ALLOC)
Bottles reserved on a SKU by an open order (VNORDREL), held in VNSKU.ALLOC and cleared when the order is invoiced (VNINVPOST).
Batch (fermentation batch)
One tank's worth of must/wine through fermentation, VNBATCH. Draws grape tonnage from one or more lots via its composition lines (VNBCOMP) and accumulates grape cost in BATCOST.
Blend (must-stage blend)
A batch drawing multiple harvest lots through its VNBCOMP lines. VINTAGE/i blends at the must stage only; there is no blending after a batch is cellared (a vessel is filled fresh by exactly one batch).
Bottling run
A draw of gallons out of a cellar vessel converted to bottles of a SKU, VNBTL. Posted by VNBTLPOST at the fixed 0.2 gal/bottle rate.
Cellar / vessel
A tank (T) or barrel (B), VNCELL. Holds CURGAL gallons at cost-per-gallon CSTGAL.
Cellar movement
A durable ledger row in VNCMOV: transfer-in (I, positive) when a batch is cellared, transfer-out (O, negative) when wine is drawn for bottling.
Cost conservation
The design property that grape cost enters at the lot and is neither invented nor lost through batch, vessel, run and bottle: a SKU's bottle cost = its runs' cost / its runs' bottles.
Fixed conversions
150 gal/ton (yield), 0.2 gal/bottle (fill), 12 bottles/case. Deliberate round numbers so every posting program's arithmetic is exact and testable.
GL feed
The SQL table VNGLFEED the monthly close posts balanced DR/CR pairs into. Not a general ledger itself — the feed a downstream GL would consume.
Idempotency
The property that re-running a posting program changes nothing already posted. Enforced by ledger keys derived from a stable business key plus a CHAIN(EN) existence check, backed by state-flag guards.
Inventory conservation
Gallons in = gallons bottled out + gallons remaining, through the cellar; bottles allocated = bottles relieved, through sales. Asserted directly by the daily battle.
Ledger key
A durable-file sequence built as base + business-digits (10M/batch, 30M/run, 50M/order, 60M/SKU) so a re-run regenerates the same key. See F.2.
Lot (harvest lot)
Tonnage received from a vineyard block, priced per tonne, VNLOT. The entry point of grape cost into the chain.
Poster (batch poster)
A parameterless batch program that scans its driving file and advances every row ready to advance (VNBATREL, VNCELLPOST, VNBTLPOST, VNORDREL, VNINVPOST, VNCSTRL, VNGLPST).
Shared symbol table
This emulator's single flat namespace for unqualified RPG field names across all open DISK files. The reason VNINV/VNAR rename their order/customer fields (F.3).
SKU (finished-goods SKU)
One bottled product, VNSKU. On-hand and allocation are in bottles; standard cost (STDCOST) is rolled up monthly.
Standard-cost roll-up
The monthly VNCSTRL step that sets each SKU's STDCOST to the weighted average cost of its completed bottling runs, writing a before/after row to VNCSTH.
State gating
Skipping rows not in the right status (BSTAT, RSTAT, OSTAT, LSTAT, BATCOST>0) and advancing them on post, so nothing posts twice by state.
Subfile
The scrolling DDS list on the VNMOVIQ screen (MSFL/MCTL), loaded after the key is read (load-then-display).