GRANARY/i — Grain Elevator Co-op

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

GRANARY/i runs the operations of a grain elevator cooperative: producer members drive loaded trucks onto the scale, each load is weighed and sampled into a scale ticket, the ticket is graded (net weight, then moisture/dockage shrink deducted to net bushels), priced against the grower's contract, and posted — creating grain inventory in a storage bin and a settlement liability the co-op owes the member. A separate settlement run pays those liabilities through the member AR/AP ledger; at fiscal year-end a patronage dividend is allocated off each member's volume of business and the general ledger closes. The logic is hand-written RPG/COBOL/CL/DDS plus a little embedded SQL, driven as three CL job cycles. This manual is the reference for the operator who runs the online screens and the periodic cycles, and for the developer maintaining the application. It is grounded entirely in the committed source (grain-app/src/sources.mjs, src/seed.mjs, and the test/gr_build.mjs / gr_daily.mjs / gr_settle.mjs / gr_annual.mjs drivers). Everything runs in library GRANARY.

Contents

A. Overview & Architecture ↑ top

A.1 What it does

GRANARY/i covers the full life of a load of grain through a country elevator:

A.2 The grain-flow architecture: files, RPG/COBOL programs, three CL cycles

Unlike a SQL-PL application, GRANARY/i keeps its business rules in hand-written RPG (and one ILE COBOL reconciliation report) operating over keyed DB2 for i physical and logical files, with a small amount of embedded SQL only where a set-based total or the GL feed makes it natural (GRGLPST). The structure has three layers:

The benefit for operations: each step is a small, re-runnable program guarded by a status flag on the record it processes, and each cycle is a single CALL that runs its steps in the fixed correct order. Every money movement lands in the durable AR/AP ledger, so a member balance and the co-op's whole position are always reconstructable.

A.3 Component & flow

  ONLINE                    DAILY (GRDAILY)          SETTLE (GRSETLC)       ANNUAL (GRYEAR)
  ------                    ---------------          ----------------       ---------------
  GRMENU (5250)             GRGRADE  grade tickets   GRSETLR pay settls     GRPATRN patronage
    1 -> GRBININQ             O -> G  (shrink)         N   -> Y (paid)         settled bu x rate
    2 -> GRTKTIQ (subfile)  GRPRICE  price + post    GRARBAL member bals    GRGLPST GL feed
                              G -> P  inventory in    GRRECON conservation   GRRECON conservation
                                      settlement out
                            GRTKTPR  status report

  SCALE TICKET (GRTKT)  O weighed-in -> G graded -> P priced/posted
       |                                    |
       |  grade: net lb / bushel wt         |  price: net bu x contract price
       v                                    v
   net bushels ------ inventory ----> GRSTK (bin grain on hand)
                 \                    GRSETL (one settlement per priced ticket)
                  +---- liability --> GRAR  (member AR/AP ledger: S credit, Y debit, P credit)
                                      GRCTR.CTFILL advanced        GRMEMB.MEQTY grown by patronage
                                      GRPATR (year-end accrual)    GRGLFEED (balanced DR/CR)

A single load flows: a scale ticket is written OPEN → GRGRADE computes shrink and net bushels, flipping it to GRADED → GRPRICE prices it against the contract, posting bushels into GRSTK, a row into GRSETL, a credit into GRAR, advancing GRCTR.CTFILL, and flipping the ticket to PRICED. The settle and annual cycles then draw the liability down and close the books — all money, never grain, so grain conservation holds throughout.

A.4 Object inventory

ObjectTypeRole
GRMEMBPFMember (producer) master, incl. paid-in equity.
GRCOMMPFCommodity master (base moisture/test-wt, shrink rates).
GRBINPFStorage bin master (capacity, committed commodity/crop).
GRSTKPFGrain inventory: bushels on hand by bin/commodity/crop.
GRCTRPFPrice contract (cash/deferred).
GRTKTPFScale ticket (the heart of the daily flow).
GRSETLPFSettlement — one row per priced ticket.
GRARPFMember AR/AP ledger (durable running balance).
GRPATRPFPatronage accrual, one per member per fiscal year.
GRSTKLF / GRCTRLF / GRTKTLF / GRSETLF / GRARLFLF (5)By-commodity, by-member and by-status access paths.
GRGLFEEDSQL tableGeneral-ledger feed (balanced DR/CR rows).
GRMENUD / GRBIND / GRTKTDDSPF (3)Menu, bin-inquiry, ticket-inquiry (subfile) displays.
GRSETPPRTFSettlement-statement printer file.
GRREFLDRPGSeed reference data (members/commodities/bins/contracts).
GRGRADERPGGrade open tickets (shrink → net bushels).
GRPRICERPGPrice graded tickets; post inventory + settlement.
GRTKTPRRPGTicket status-summary report.
GRSETLRRPGSettlement run — pay unpaid settlements.
GRARBALRPGMember AR/AP balance report.
GRPATRNRPGPatronage dividend allocation.
GRGLPSTSQLRPGLEAnnual GL post (embedded SQL over the GL feed).
GRBININQ / GRTKTIQ / GRMENURPG (3)Interactive inquiry / menu programs.
GRRECONILE COBOLGrain-conservation reconciliation report.
GRSETUPCLCreate objects + compile programs.
GRDAILY / GRSETLC / GRYEARCL (3)The three job cycles.

The full catalogue is 9 PFs + 5 LFs + 1 SQL table, 3 DSPFs + 1 PRTF, 11 RPG programs, 1 COBOL program and 4 CL programs. Sections D and F expand each.

B. Online Transactions & Screens ↑ top

B.1 The command/entry line

GRANARY/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 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 GRANARY — the tested jobs run with LIBL = QSYS QGPL GRANARY QTEMP and CURLIB = GRANARY.

To do thisType on the command line
Open the main menu (then 1=bins, 2=tickets)CALL GRANARY/GRMENU
Open the bin / grain-inventory inquiry directlyCALL GRANARY/GRBININQ
Open the scale-ticket inquiry (subfile) directlyCALL GRANARY/GRTKTIQ
Run the daily grade-price-inventory cycleCALL GRANARY/GRDAILY (or SBMJOB it)
Run the settlement cycleCALL GRANARY/GRSETLC
Run the annual patronage + GL closeCALL GRANARY/GRYEAR
(First-time) create objects & seed reference dataCALL GRANARY/GRSETUP then CALL GRANARY/GRREFLD

The batch cycles and programs take no CALL parameters — each processes whatever records are in the right status, so a scheduled submission is a bare CALL. Only GRMENU, GRBININQ and GRTKTIQ are interactive; the batch programs run to completion and DSPLY a one-line result per step (e.g. GRGRADE GRADED= 3 SKIP= 0).

Scale tickets are weighed in (the OPEN row is created) at the scale house. In this build the OPEN ticket is written to GRTKT directly (the test drivers write it with pf.writeRecords); there is no separate interactive weigh-in screen program in v1 — GRTKTIQ is inquiry-only. Grading and pricing of those tickets is the daily batch chain.

B.2 The menu & inquiry screens

GRMENU / GRMENUD — main menu

GRMENU presents the two-option menu and dispatches by calling the chosen inquiry program. Option 1 calls GRBININQ; option 2 calls GRTKTIQ; any other non-blank option shows Invalid option. F3 exits.

GRANARY/i Main Menu 1. Bin / Inventory Inquiry 2. Scale Ticket Inquiry Option . . . . : _ F3=Exit Enter=Select

GRBININQ / GRBIND — bin / grain-inventory inquiry (plain screen)

The operator keys an elevator and bin; the program CHAINs the bin master (GRBIN) and walks the bin's grain-inventory rows (GRSTK) to show what it holds and the fill percent against capacity. A bin that exists but has no inventory shows Bin found -- no inventory yet.; an unknown bin shows Bin not found.

Bin Inquiry - GRANARY/i Elevator: ELV1 Bin: A-100 Description . : HOUSE BIN A-100 Capacity . . . : 50,000.00 Commodity . . : CORN Crop year . . : 2025 Bushels on hand: 962.00 Fill percent . : 1.92% Bin found. F3=Exit Enter=Inquire
FieldType (DDS)Shows
IELEV / IBIN4A / 6A inputKeyed elevator and bin.
DDESC20A outputBin description.
DCAP14A outputCapacity in bushels (edited).
DCOMM / DCROP4A / 6A outputCommitted commodity & crop year.
DQTY14A outputBushels on hand (from GRSTK.GQTY).
DPCT14A outputFill percent = on-hand / capacity × 100.
DMSG50A outputStatus message line.

GRTKTIQ / GRTKTD — scale-ticket inquiry (subfile)

GRTKTIQ presents a subfile (DDS record TSFL under control record TCTL, SFLPAG(5) per page, SFLSIZ(20)) of scale tickets, filtered by the status the operator keys in Status filter: O open, G graded, P priced, or * for all. Each pass clears and reloads the subfile from the by-status/date logical file (GRTKTLF), so the filter takes effect immediately, and shows the count and total net bushels matched.

Scale Ticket Inquiry - GRANARY/i Status filter (O/G/P/*): * Tickets . . . : 3 Net bushels . : 1,945.00 Ticket Member Comm Net bushels S T0000001 M00001 CORN 962.00 P T0000002 M00003 SOYB 983.00 P T0000003 M00002 CORN 500.00 G Tickets found. F3=Exit Roll=Page Enter=Inquire
FieldType (DDS)Shows
IFILT1A inputStatus filter: O / G / P / *.
STKT8A outputTicket number.
SMEMB6A outputMember number.
SCOMM4A outputCommodity.
SNETBU12A outputNet bushels (from TNETBU, edited).
SSTAT1A outputTicket status O/G/P/X.
DTCNT / DTBU12A outputMatched count / total net bushels.

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

Honest statement: GRANARY/i does not model a true four-eyes maker–checker / separate-authorization workflow. There is no "one clerk grades, a second approves" or "one user prices, a second releases the payment" step in the code: the daily chain grades and prices in one run, and the settlement run pays every unpaid settlement without a separate release. The manual documents the control model the application does have:

In sum, the control posture is per-record status gating + a durable signed ledger + stable-key idempotency + a grain-conservation cross-check, all enforced in the programs and files, rather than a segregation-of-duties approval workflow.

C. Batch Jobs & the Periodic Cycle ↑ top

GRANARY/i's processing runs as three CL job cycles rather than one monolithic job, matching a country elevator's rhythm: a daily grade-price-inventory chain as trucks come off the scale (GRDAILY), a settlement run that pays members (GRSETLC), and an annual patronage allocation + GL close (GRYEAR). Every batch program takes no CALL parameters — each simply processes whatever records are in the right status — so a scheduled submission is a bare CALL (or SBMJOB). There is no control table: the fiscal year and patronage rate that the annual programs need are compiled constants (GRPATRN uses wfy = 2025, wrate = 0.1500; GRGLPST uses wbatch = 202512), so the co-op re-hosts a new year by rebuilding those two programs.

-- run a cycle directly, or submit it to a job queue
CALL PGM(GRANARY/GRDAILY)
SBMJOB CMD(CALL PGM(GRANARY/GRSETLC)) JOB(GRSETLC)
SBMJOB CMD(CALL PGM(GRANARY/GRYEAR))  JOB(GRYEAR)

C.1 Full batch program set

ProgramTypeCyclePurposeReads / WritesDSPLY result line
GRREFLDRPGsetup Seed reference data. Writes GRMEMB (4), GRCOMM (3), GRBIN (5), GRCTR (4). GRREFLD MEMBERS=4 COMMS=3 BINS=5 CONTRACTS=4
GRGRADERPGdaily 1 Grade every OPEN ticket: net lb → raw bushels, deduct moisture/dockage shrink → net bushels. Reads GRCOMM; updates GRTKT (TBU/TSHKBU/TNETBU, TSTAT O→G). GRGRADE GRADED=n SKIP=n
GRPRICERPGdaily 2 Price every GRADED ticket against its contract; post inventory + settlement + ledger credit; advance fill. Reads GRCTR/GRBIN; writes GRSTK, GRSETL, GRAR(S); updates GRCTR.CTFILL, GRTKT (G→P). GRPRICE PRICED=n DEFERRED=n SKIP=n
GRTKTPRRPGdaily 3 Ticket status-summary report (counts by status + priced net bushels). Reads GRTKT. No file writes. GRTKTPR OPEN=n GRADED=n PRICED=n NETBU=n
GRSETLRRPGsettle 1 Pay every unpaid settlement: post a debit ledger row, flip SPDFLG N→Y. Reads/updates GRSETL; writes GRAR(Y). GRSETLR PAID=n TOTAL=n SKIP=n
GRARBALRPGsettle 2 Member AR/AP balance report (running sum per member, control-break). Reads GRARLF, GRMEMB. No file writes. GRARBAL MEMB=m BALANCE=n per member, then MEMBERS=n GRANDTOTAL=n
GRPATRNRPGannual 1 Allocate a flat per-bushel patronage dividend off each active member's settled-bushel volume. Reads GRSETL (by member); writes GRPATR, GRAR(P); grows GRMEMB.MEQTY. GRPATRN ALLOCATED=n SKIP=n
GRGLPSTSQLRPGLEannual 2 Sum the AR ledger by type; post balanced DR/CR pairs into the SQL GL feed (delta since last close). Reads GRAR; INSERTs into GRGLFEED. GRGLPST BATCH=202512 ROWS=n
GRRECONILE COBOLsettle 3 / annual 3 Grain-conservation reconciliation: ticketed-in bushels vs stored bushels. Reads GRTKT, GRSTK. No file writes. GRRECON TICKETED n / STORED n / TICKETS n / BINROWS n

The three cycle drivers (GRDAILY, GRSETLC, GRYEAR) each ADDLIBLE GRANARY then chain their steps with plain CALL PGM(...) and end with a SNDPGMMSG completion banner. GRSETUP is the one-time object-build CL (delete + CRTPF/CRTLF/CRTDSPF/CRTPRTF/RUNSQLSTM + CRTBNDRPG/CRTBNDCBL for every member).

C.2 Daily / settle / annual detail

Daily — GRDAILY (GRGRADE → GRPRICE → GRTKTPR)

GRGRADE walks GRTKT and grades every TSTAT='O' ticket: TBU = TNET / CBASETW (net pounds ÷ the commodity's legal bushel weight), then subtracts shrink bushels for moisture over CBASEM and dockage over CDOCKPT (TSHKBU = TBU × (moist-pts × CSHMOIS + dock-pts × CSHDOCK)), giving TNETBU; it flips the ticket to 'G'. GRPRICE then prices every 'G' ticket against its contract: a cash contract prices at CTPRICE; a deferred contract still carrying CTSTAT='D' is skipped (counted as DEFERRED, left graded). A priced ticket posts TNETBU into the bin's GRSTK row, writes one GRSETL settlement (SNETAMT = TNETBU × SPRICE) and one GRAR credit (LTYPE='S'), advances CTFILL, and flips the ticket to 'P'. GRTKTPR prints the resulting status counts.

Expected DSPLY (the tested 3-ticket day: two cash, one deferred-unpriced):
  GRGRADE GRADED=3 SKIP=0
  GRPRICE PRICED=2 DEFERRED=1 SKIP=0    T0000003's deferred contract is unpriced
  GRTKTPR OPEN=0 GRADED=1 PRICED=2 NETBU=1945.00   962.00 + 983.00

Settle — GRSETLC (GRSETLR → GRARBAL → GRRECON)

GRSETLR walks GRSETL and pays every SPDFLG='N' settlement: it posts a negative ('Y', debit) GRAR row for SNETAMT and flips SPDFLG to 'Y'. Because the settlement credit and the payment debit are equal and opposite, a fully-paid member's running ledger balance nets to exactly zero. GRARBAL then reports each member's balance by control-break over GRARLF, and GRRECON cross-checks grain conservation.

Expected DSPLY (paying the two tested settlements):
  GRSETLR PAID=2 TOTAL=15387.75 SKIP=0   4329.00 + 11058.75
  GRARBAL MEMB=M00001 BALANCE=0.00       credit + payment net to zero
  GRARBAL MEMB=M00003 BALANCE=0.00
  GRARBAL MEMBERS=2 GRANDTOTAL=0.00
  GRRECON TICKETED     1945.00           settlement moved money, not grain
  GRRECON STORED       1945.00

Annual — GRYEAR (GRPATRN → GRGLPST → GRRECON)

GRPATRN reads every member, sums that member's settled bushels for fiscal year 2025 (walking the member-keyed settlement path, filtering on %int(sdt/10000)=wfy), and allocates PALLOC = PBU × 0.1500. It writes a GRPATR accrual, a GRAR credit ('P'), and grows MEQTY by the same amount. Only active (MSTAT='A') members with settled bushels accrue. GRGLPST then totals the whole AR ledger by type and posts balanced DR/CR pairs (see F.5), and GRRECON re-checks conservation.

Expected DSPLY (the tested year: M00001 962 bu, M00003 983 bu settled):
  GRPATRN ALLOCATED=2 SKIP=2   M00002 (unpriced) & M00004 (no tickets) skip
       M00001: 962.00 x 0.1500 = 144.30 -> equity 12500.00 -> 12644.30
       M00003: 983.00 x 0.1500 = 147.45 -> equity  3200.00 ->  3347.45
  GRGLPST BATCH=202512 ROWS=6
  GRRECON TICKETED     1945.00   patronage/GL moved money and equity, not grain
  GRRECON STORED       1945.00

C.3 Ordering & dependencies

D. Data Files (data dictionary) ↑ top

All files are in library GRANARY, grounded in the DDS in src/sources.mjs. Dates are packed/signed numerics in YYYYMMDD form (4S 0 crop year is YYYY); money and quantities are packed decimal (P); prices and shrink rates carry 4 decimal places so fractional cents and fractional-percent shrink factors are exact. Nine physical files, five logical (alternate-key) files and one SQL table.

GRMEMB — Member (producer) master (PK MEMBNO)

FieldTypeMeaning
MEMBNO6AMember number (PK, e.g. M00001).
MEMBNM25AMember name.
MADDR25AAddress.
MTYPE1AP = patron/producer member.
MEQTY11P 2Paid-in equity (patronage capital); grown by the annual patronage run.
MSTAT1AA = active (only active members accrue patronage).

GRCOMM — Commodity master (PK COMM)

FieldTypeMeaning
COMM4ACommodity code (PK: CORN, SOYB, WHET).
CDESC20ADescription.
CBASEM5P 2Base (no-discount) moisture percent.
CBASETW5P 2Legal bushel weight, lb/bu (the pound→bushel divisor).
CDOCKPT5P 2Dockage-free threshold percent; shrink applies above it.
CSHMOIS7P 4Moisture shrink rate: fraction of bushels lost per point over base.
CSHDOCK7P 4Dockage shrink rate: fraction per point over the threshold.
CSTAT1AA = active.

Seeded set: CORN 15.00%/56.00lb/2.00%/.0140/.0100; SOYB 13.00%/60.00lb/1.00%/.0120/.0100; WHET 13.50%/60.00lb/1.00%/.0130/.0100.

GRBIN — Storage bin master (PK ELEV+BIN)

FieldTypeMeaning
ELEV4AElevator id (PK part 1, e.g. ELV1).
BIN6ABin id (PK part 2, e.g. A-100).
BDESC20ABin description.
BCAP11P 2Capacity in bushels.
BCOMM4ACommitted commodity (blank = unassigned annex); a bin holds one commodity/crop at a time.
BCROP4S 0Committed crop year.
BSTAT1AA = active.

GRSTK — Grain inventory (PK ELEV+BIN+COMM+CROP)

FieldTypeMeaning
ELEV / BIN / COMM / CROP4A / 6A / 4A / 4S 0Composite key: bushels on hand by bin, commodity, crop year.
GQTY11P 2Net (post-shrink) bushels currently stored — the co-op's physical grain position.
GLSTDT8S 0Last-updated ticket date (YYYYMMDD).
GSTAT1AA = active.

GRCTR — Price contract (PK CTRNO)

FieldTypeMeaning
CTRNO8AContract number (PK, e.g. CT000001).
MEMBNO6AOwning member.
COMM / CROP4A / 4S 0Commodity and crop year contracted.
CTTYPE1AC = cash (priced at CTPRICE), D = deferred (priced later).
CTPRICE9P 4Contract price per bushel (0 on an unpriced deferred contract).
CTQTY11P 2Bushels contracted.
CTFILL11P 2Bushels delivered against it so far (advanced by GRPRICE).
CTSTAT1AO = open/priced, D = deferred and not yet priced (tickets cannot settle).

GRTKT — Scale ticket (PK TKTNO)

FieldTypeMeaning
TKTNO8ATicket number (PK, e.g. T0000001).
TKTDT8S 0Ticket date (YYYYMMDD).
MEMBNO / CTRNO6A / 8AMember and the contract this load settles against.
COMM / CROP4A / 4S 0Commodity and crop year.
ELEV / BIN4A / 6ABin the load unloaded to.
TGROSS / TTARE / TNET9P 0Gross, tare, net weight in pounds off the scale.
TMOIS / TTESTWT / TDOCKPT5P 2Sampled moisture %, test weight lb/bu, dockage %.
TBU11P 2Raw bushels (TNET / CBASETW), set by grading.
TSHKBU11P 2Shrink bushels deducted, set by grading.
TNETBU11P 2Net bushels after shrink — what gets inventoried and paid.
TSTAT1AO open/weighed-in, G graded, P priced/posted, X cancelled.

GRSETL — Settlement (PK SETLNO) — one row per priced ticket

FieldTypeMeaning
SETLNO8ASettlement number (PK); derived from the ticket number ('S' + the ticket's 7-digit tail).
TKTNO / MEMBNO / CTRNO8A / 6A / 8ASource ticket, member, contract.
SDT8S 0Settlement date (the ticket date).
SBU11P 2Net bushels settled.
SPRICE9P 4Price per bushel (the contract price).
SAMT13P 2Gross amount (SBU × SPRICE).
SDISC13P 2Additional dollar discount (reserved; currently 0).
SNETAMT13P 2Amount owed the member (SAMT - SDISC).
SPDFLG1AN = booked/unpaid, Y = paid by the settlement run.

GRAR — Member AR/AP ledger (PK LSEQ) — the durable running balance

FieldTypeMeaning
LSEQ8S 0Ledger sequence (PK, a stable derived key — see F.3).
MEMBNO6AMember.
LTYPE1AS settlement liability (credit, +), Y payment (debit, −), P patronage accrual (credit, +).
LDT8S 0Posting date.
LAMT13P 2Signed amount; a member's balance is the running sum of their LAMT.
LREF8AReference (ticket or settlement number, or PATRON).
LMEMO25AHuman-readable memo.

GRPATR — Patronage accrual (PK FYEAR+MEMBNO)

FieldTypeMeaning
FYEAR / MEMBNO4S 0 / 6AComposite key: one accrual per member per fiscal year.
PBU11P 2Member's settled bushels that year (volume of business).
PALLOC13P 2Dollar dividend allocated (PBU × rate).
PSTAT1AO open, P posted to equity + AR ledger.

GRGLFEED — SQL GL feed (PK GLSEQ)

FieldTypeMeaning
GLSEQDECIMAL(8,0)Row sequence (PK); continues across closes so a re-run posts only the delta.
GLBATCHDECIMAL(6,0)Close batch (YYYYMM, e.g. 202512).
ACCTCHAR(9)GL account (see F.5).
DRCRCHAR(1)D debit / C credit.
AMTDECIMAL(11,2)Amount (always positive; direction is in DRCR).
GLREF / GLDTCHAR(8) / DECIMAL(8,0)Reference (SETTLE/PAYMENT/PATRON) and date. Index GRGLFACC on (ACCT, DRCR).

Logical files (alternate access paths)

LFOverLeads onAnswers
GRSTKLFGRSTKCOMM, CROP, ELEV, BIN"How much of this commodity do we hold everywhere?"
GRCTRLFGRCTRMEMBNO, CTRNOEvery contract a member holds.
GRTKTLFGRTKTTSTAT, TKTDT, TKTNOTickets ready for a step / by status (the subfile inquiry).
GRSETLFGRSETLMEMBNO, SETLNOA grower's settlement history (patronage bushel sum).
GRARLFGRARMEMBNO, LSEQA member's ledger for balance/statement (GRARBAL).

E. Operations Runbook ↑ top

The worked figures below are the exact ones the test drivers hand-derive and assert (test/gr_daily.mjs, gr_settle.mjs, gr_annual.mjs). They make the best reconciling worked-example: a single day of two cash tickets and one deferred-unpriced ticket, carried all the way through settlement and the annual close.

E.1 Day-in-the-life

Trucks come off the scale and their OPEN scale tickets are written to GRTKT (in this build the weigh-in row is written directly — there is no interactive weigh-in screen; B.1 note). Then run the daily cycle:

-- first-time only: build the objects and seed reference data
CALL GRANARY/GRSETUP
CALL GRANARY/GRREFLD

-- each day, after the day's OPEN tickets are on GRTKT:
CALL GRANARY/GRDAILY        GRGRADE -> GRPRICE -> GRTKTPR

The tested day's three tickets:

TicketMember / ContractComm / BinNet lbRaw buShrink buNet buPriced?
T0000001M00001 / CT000001 cash @4.5000CORN / A-10056,0001000.0038.00962.00yes → $4,329.00
T0000002M00003 / CT000003 cash @11.2500SOYB / B-20060,0001000.0017.00983.00yes → $11,058.75
T0000003M00002 / CT000002 deferred, unpricedCORN / A-10128,000500.000.00500.00no (stays G)

Shrink worked: T0000001 moisture 17.0% is 2.0 pts over CORN base 15.0 → 2.0×.0140=.0280; dockage 3.0% is 1.0 pt over threshold 2.0 → 1.0×.0100=.0100; total .0380 × 1000 = 38.00 bu. T0000003 sits exactly at base moisture (15.0%) and exactly at the dockage threshold (2.0%), so zero shrink either way. Its deferred contract is still CTSTAT='D', so GRPRICE defers it: no inventory, no settlement, no ledger row; the ticket stays graded.

After GRDAILY: bin A-100 holds 962.00 bu, B-200 holds 983.00 bu, A-101 holds nothing; GRSETL has 2 unpaid rows; GRAR has 2 'S' credit rows (M00001 +4329.00, M00003 +11058.75); CT000001.CTFILL=962.00, CT000003.CTFILL=983.00, CT000002.CTFILL=0. GRTKTPR reports OPEN=0 GRADED=1 PRICED=2 NETBU=1945.00.

E.2 Settlement & year-end close

CALL GRANARY/GRSETLC        GRSETLR -> GRARBAL -> GRRECON
CALL GRANARY/GRYEAR         GRPATRN -> GRGLPST -> GRRECON

Settlement. GRSETLR pays both unpaid settlements — PAID=2 TOTAL=15387.75 (4329.00 + 11058.75) — posting a 'Y' debit of −4329.00 for M00001 and −11058.75 for M00003 and flipping both SPDFLG to Y. Each member's running balance now nets to exactly zero (credit + equal debit), which GRARBAL confirms: MEMBERS=2 GRANDTOTAL=0.00.

Year-end. GRPATRN allocates a flat 0.1500/bu patronage off each active member's settled fiscal-2025 bushels — ALLOCATED=2 SKIP=2 (M00002 was never priced, M00004 had no tickets):

MemberSettled bu (PBU)RatePALLOCEquity before → after
M00001962.000.1500$144.3012,500.00 → 12,644.30
M00003983.000.1500$147.453,200.00 → 3,347.45

Then GRGLPST posts the annual GL batch 202512 (six rows, three balanced DR/CR pairs — see F.5) and GRRECON re-confirms conservation.

E.3 Reconciling figures & the grain-conservation invariant

Three invariants must hold after every cycle; the operator can read them straight off the DSPLY lines and the reports:

If GRRECON shows TICKETED ≠ STORED, something wrote to GRSTK outside GRPRICE, or a ticket flipped to 'P' without its inventory posting — investigate before running settlement. The three cycles are each safely re-runnable, so the safe recovery for a mid-cycle failure is simply to re-CALL the cycle: the status flags and stable-key guards (F.3) skip everything already posted and finish the rest.

F. Developer Reference ↑ top

F.1 The grade & price formulas

Grading (GRGRADE). Net pounds come off the scale as TNET = TGROSS - TTARE (written on the ticket). Raw bushels convert at the commodity's legal bushel weight, not the load's tested weight:

  TBU     = TNET / CBASETW                       e.g. 56000 / 56.00 = 1000.00
  moispts = max(TMOIS - CBASEM, 0)               points over base moisture
  dockpts = max(TDOCKPT - CDOCKPT, 0)            points over the dockage-free threshold
  TSHKBU  = TBU * (moispts*CSHMOIS + dockpts*CSHDOCK)
  TNETBU  = max(TBU - TSHKBU, 0)                 clamped non-negative

Test weight (TTESTWT) is sampled and stored but does not enter the pound-to-bushel conversion in this model — a bushel is fixed by the commodity's legal weight (CBASETW); test weight is a grade/discount input reserved for future quality discounting. A commodity with CBASETW ≤ 0, or a ticket whose commodity is not found, is skipped (counted in SKIP), never divided by zero.

Pricing (GRPRICE). A graded ticket prices at its contract price: SNETAMT = TNETBU × CTPRICE. A deferred contract still carrying CTTYPE='D' and CTSTAT='D' is deferred (counted in DEFERRED, ticket left graded). Otherwise pricing performs five posts in order: (a) add TNETBU to GRSTK (write the bin row if new); (b) write the GRSETL row; (c) write the GRAR 'S' credit; (d) advance CTFILL; (e) flip TSTAT to 'P'. Each of (d) and (e) re-CHAINs its record defensively, since the intervening writes moved the file cursor.

F.2 Programs

ProgramFiles (F-spec)Key logic
GRGRADEGRTKT (UF), GRCOMM (IF)Walk *LOVAL; grade every 'O' ticket via GRD1; flip to 'G'.
GRPRICEGRTKT/GRCTR (UF), GRSTK/GRSETL/GRAR (UF A), GRBIN (IF)Price every 'G' ticket via PRC1; the five-post sequence; stable settlement + ledger keys.
GRTKTPRGRTKT (IF)Count tickets by status; sum TNETBU on priced ones.
GRSETLRGRSETL (UF), GRAR (UF A)Pay every SPDFLG='N' row via PAY1; write 'Y' debit; flip to 'Y'.
GRARBALGRARLF (IF), GRMEMB (IF)Control-break running-balance report; FLUSH per member. (Control-break bug fixed — see the source comment.)
GRPATRNGRMEMB (UF), GRSETLF (IF), GRPATR/GRAR (UF A)Per active member, sum settled fiscal-year bushels (READE on member key); allocate; write accrual + 'P' ledger; grow MEQTY.
GRGLPSTGRAR (IF) + embedded SQL over GRGLFEEDSum ledger by type; post delta DR/CR pairs; continue GLSEQ from prior close.
GRBININQ / GRTKTIQ / GRMENUWORKSTN + keyed DISKInteractive inquiry / menu (B.2).
GRRECONGRTKT, GRSTK (COBOL indexed)Sum ticketed-in vs stored; DISPLAY both.

F.3 Idempotency & ledger keys

Every posting program derives a stable key from the record it processes and refuses re-entry with a CHAIN(EN) guard, so any cycle is safely re-runnable. The CHAIN(EN) polarity is the standard one: the indicator is ON when the row is NOT found (genuinely new → write) and OFF when it IS found (already posted → skip).

ProgramStable keySecond guard
GRPRICESettlement SETLNO = 'S' + substr(TKTNO,2,7); ledger LSEQ = 10000000 + ticket-tail.TSTAT='P'.
GRSETLRLedger LSEQ = 20000000 + ticket-tail.SPDFLG='Y'.
GRPATRNGRPATR keyed (FYEAR, MEMBNO); ledger LSEQ = 30000000 + FYEAR*1000 + per-member seq.the (FYEAR,MEMBNO) accrual key.
GRGLPSTGLSEQ continues from max(GLSEQ); posts only the movement vs prior-posted per-account totals.primary key on GLSEQ.
Historical bug, fixed and documented in source. An earlier GRPRICE built the settlement key with 'S' + %editc(wtktnum:'X'). The X edit code renders the field's full declared width (15 digits for a 7S0), so the 16-char result truncated into the 8A field always collapsed to 'S0000000' — every ticket collided on one key and pricing silently stopped after ticket one. The fix substrings the ticket's own 7 digits straight across (%subst(wtkt:2:7)), avoiding the width mismatch. GRARBAL likewise had a control-break bug (WMEMB stuck on the first member) since fixed.

F.4 Engine-compatibility notes

F.5 GL account map

GRGLPST sums the AR ledger by LTYPE and posts three balanced pairs:

Ledger sourceDebitCreditTested amount
Settlement liability ('S')1400-INV2100-APM15,387.75
Payments ('Y', ledger holds negative)2100-APM1000-CSH15,387.75
Patronage accrued ('P')6100-PEX2900-PEQ291.75

1400-INV grain inventory/purchases, 2100-APM accounts payable – members, 1000-CSH cash, 6100-PEX patronage expense, 2900-PEQ member equity – patronage payable. Total debits 31,067.25 = total credits 31,067.25.

G. Glossary ↑ top

Scale ticket (GRTKT)
The record of one load of grain: gross/tare/net weight, sampled moisture/test-weight/dockage, and the computed bushels. Moves O (weighed in) → G (graded) → P (priced/posted).
Gross / Tare / Net
Loaded weight, empty-truck weight, and their difference (net pounds of grain) — all in pounds off the scale.
Grading
Converting net pounds to raw bushels at the commodity's legal bushel weight, then deducting shrink, yielding net bushels.
Shrink
Bushels deducted for moisture above the base and dockage above the free threshold, at the commodity's per-point shrink rates. Reflects that wet/dirty grain yields fewer marketable bushels.
Net bushels (TNETBU)
Raw bushels minus shrink — the bushels that actually get inventoried and paid.
Bushel / legal bushel weight (CBASETW)
A bushel is defined by a commodity's standard test weight (corn 56 lb, soybeans/wheat 60 lb). That fixed weight is the pound-to-bushel divisor.
Contract (GRCTR)
A price agreement: cash (priced now at CTPRICE) or deferred (grain stored, priced later; unpriced deferred contracts cannot settle).
Bin / grain inventory (GRBIN / GRSTK)
A storage bin holds one commodity/crop at a time; its GRSTK row is the net bushels on hand — the co-op's physical grain position.
Settlement (GRSETL)
One row per priced ticket recording what the co-op owes the grower (net bushels × price). Booked unpaid (SPDFLG='N'), then paid by the settlement run ('Y').
AR/AP ledger (GRAR)
The durable, signed, sequence-keyed running-balance trail: S settlement credit, Y payment debit, P patronage credit. A member's balance is the running sum.
Patronage dividend (GRPATR)
A co-op returns a share of margins to members by volume of business. Here: a flat per-bushel rate on each active member's settled fiscal-year bushels, accrued to member equity (MEQTY).
Member equity (MEQTY)
A member's paid-in patronage capital, grown by each year's patronage accrual.
Grain conservation
The invariant that bushels ticketed in equal bushels stored; money and equity move in settlement and the close, grain does not. Cross-checked by GRRECON.
GL feed (GRGLFEED)
The general-ledger feed table the annual close posts balanced debit/credit pairs into; every batch sums to zero.
Cycle
One of the three CL job chains: GRDAILY (grade/price), GRSETLC (settle), GRYEAR (patronage + GL close).
Idempotency guard
A stable derived key plus a status flag that lets any cycle be re-run without double-posting.