LEDGER/i — General Ledger & Financial Close

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

LEDGER/i is a double-entry general-ledger and financial-close application: chart of accounts, journal-batch capture and validation, posting to the ledger and to account/period balances, a trial balance re-derived from the detail, recurring and accrual journals, period close, financial statement generation (P&L / balance sheet) and the year-end retained-earnings roll. The business logic is hand-written RPG (fixed-form C-specs plus /free) over keyed DDS physical and logical files, with one ILE COBOL statement generator and one SQLRPGLE summary-publish program. One accounting invariant is enforced everywhere: for every posted document SUM(DR) = SUM(CR), and because every ledger amount is stored debit-positive, SUM(GAMT) over the whole ledger is exactly zero. This manual is the reference for the operator who runs the four job cycles and the online screens, and for the developer maintaining the application. It is grounded entirely in the committed source (gl-app/src/sources.mjs, gl-app/src/seed.mjs, and the gl-app/test/gl_*.mjs drivers).

Contents

A. Overview & Architecture ↑ top

A.1 What it does

LEDGER/i runs the full life of a general ledger as four periodic job cycles:

A.2 Sign convention & the balance invariant

Two storage decisions carry the whole design and are worth stating up front:

Everything runs in library LEDGER. The tested jobs run with LIBL = QSYS QGPL LEDGER QTEMP and CURLIB = LEDGER.

A.3 Component & flow

  DAILY  GLDAILY          WEEKLY GLWEEK           MONTHLY GLMONTH        ANNUAL GLYEAR
  --------------          --------------          ---------------        --------------
  GLJEGEN  (capture)      GLRECUR (gen R+A)       GLREVACC (reverse A)   GLYRCLS (close P&L->RE)
  GLEDIT   (validate)     GLPOST  (post)          GLTRIAL  (re-derive)   GLNEWYR (open new FY)
  GLPOST   (post)         GLTRIAL (re-derive)     GLCLOSE  (lock period) GLBALCK (prove)
  GLBALCK  (prove)        GLTBPR  (print TB)      GLSUMPB  (SQL extract) GLFINST (statements)
                                                  GLFINST  (statements)

     ONLINE (5250)                 files touched by the batch chain
     -------------                 --------------------------------
     GLMENU                        GLBAT  batch header (control totals)
       1 -> GLACCTIQ (GLACCTD)     GLJHDR journal header   GLJDTL journal lines
       2 -> GLJRNIQ  (GLJRND SFL)  GLLEDG posted ledger    GLBAL  period balances
                                   GLACCT chart (ACBAL)    GLPERD period calendar
                                   GLRECR templates        GLFSLN statement layout
                                   GLCLOG close log        GLSUMM SQL summary extract

A single daily event flows: GLJEGEN writes journal headers/lines at JSTAT='E' and recomputes the batch control totals → GLEDIT validates each document to 'V' or 'X' and rejects any batch carrying a failure → GLPOST turns each 'V' document's lines into GLLEDG rows, moves ACBAL and accumulates GLBAL, then flips the document to 'P'GLBALCK re-derives SUM(DR) vs SUM(CR) from the ledger and reports OK/BAD.

A.4 Object inventory

ObjectTypeRole
GLACCTPFChart of accounts (the spine; carries ACBAL).
GLACCLFLFChart keyed by ATYPE then ACCTNO (statement order).
GLPERDPFAccounting-period calendar (open/closed/future).
GLBATPFJournal-batch header + control totals.
GLJHDRPFJournal entry header (one per document).
GLJDTLPFJournal entry lines (unsigned magnitude + side).
GLJDLFLFJournal lines keyed by ACCTNO (account-activity path).
GLLEDGPFPosted GL ledger (durable, debit-positive audit trail).
GLBALPFPeriod balances by (year, period, account).
GLRECRPFRecurring/accrual journal templates.
GLFSLNPFFinancial-statement line layout.
GLCLOGPFPeriod-close log.
GLSUMMSQL tableSummarised GL extract for downstream consolidation.
GLACCTD / GLJRND / GLMENUDDSPFAccount inquiry / journal subfile / menu.
GLTBPPRTFTrial-balance printer file.
GL* (13)RPGLEReference load, capture, edit, post, balance-check, recurring, trial, print, reverse, close, year-close, new-year, plus 3 online programs.
GLSUMPBSQLRPGLEPublish the closed period into the SQL extract.
GLFINSTILE COBOLFinancial statement generator (assets=liab+equity+NI).
GLSETUPCLCreate every object & compile every program.
GLDAILY / GLWEEK / GLMONTH / GLYEARCLThe four job cycles.

The catalogue is 10 PFs + 2 LFs + 3 DSPFs + 1 PRTF + 1 SQL table + 1 SQL index, driven by 16 RPG programs (15 RPGLE + 1 SQLRPGLE-flavoured GLSUMPB), 1 COBOL program and 5 CL programs. Sections D and F expand each.

B. Online Transactions & Screens ↑ top

B.1 The command/entry line

LEDGER/i has no CICS transaction identifiers. 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". The job's library list must include LEDGER.

To do thisType on the command line
Open the operator main menuCALL LEDGER/GLMENU
Open the account inquiry screen directlyCALL LEDGER/GLACCTIQ
Open the journal-line subfile inquiry directlyCALL LEDGER/GLJRNIQ
Run the daily cycleCALL LEDGER/GLDAILY (or SBMJOB it)
Run the weekly cycleCALL LEDGER/GLWEEK
Run the monthly closeCALL LEDGER/GLMONTH
Run the annual rollCALL LEDGER/GLYEAR
Build/compile the whole application (once)CALL LEDGER/GLSETUP

All batch programs take no CALL parameters. Notably the app carries no online control-date table: the seeded worked example is hard-wired to FY2026, period 1 (GLJEGEN, GLRECUR, GLTRIAL, GLCLOSE and GLSUMPB all set wfy=2026; wpn=1 in-line), and the annual programs target FY2026→FY2027. This is an honest limitation of the sample: the periodicity is real (four distinct cycles) but the processing period is coded, not operator-supplied. See section F.3.

B.2 The menu & inquiry screens

Three interactive programs exist. GLMENU (over GLMENUD) is the driver; option 1 program-CALLs GLACCTIQ, option 2 program-CALLs GLJRNIQ, any other non-blank option shows Invalid option. and stays on the menu. F3 (CA03) returns from an inquiry to the menu, and again from the menu ends the session.

LEDGER/i Main Menu 1. Account Inquiry 2. Journal Entry Inquiry Option . . . . : _ F3=Exit Enter=Select

GLACCTIQ — account inquiry (plain DSPF, GLACCTD)

A non-subfile screen. Key an account number into IACCT and Enter; the program CHAINs GLACCT and renders the master record plus two balances: the stored Ledger balance (DBAL, the raw debit-positive ACBAL) and the Normal-side bal (DNBAL, derived — a credit-normal account's ACBAL is negated so an accountant reads it in its own sense). A miss clears every output field and shows Account not found.

Account Inquiry - LEDGER/i Account number: 400000 Description . : SALES REVENUE Account type . : R Normal side . : C Ledger balance : 15,500.00- <- stored debit-positive (trailing minus) Normal-side bal: 15,500.00 <- flipped for a credit-normal account Postable . . . : Y Status . . . . : A F3=Exit Enter=Inquire
FieldType (DDS)Shows
IACCT6A input/outputAccount number to look up.
DDESC / DTYPE / DNORM30A / 1A / 1A outputDescription, account type (A/L/Q/R/E), normal side (D/C).
DBAL14A outputStored ACBAL, edit-code K (thousands, trailing minus).
DNBAL14A outputNormal-side presentation balance (credit-normal accounts flipped).
DPOST / DSTAT1A outputPostable flag / account status.
DMSG50A outputFound / not-found message (highlighted).

GLJRNIQ — journal inquiry with a line subfile (GLJRND)

The app's subfile screen. Key a document number into IDOCNO; the program CHAINs GLJHDR for the header (description and the recomputed control totals), then SETLL/READE-walks GLJDTL to load one subfile row (JSFL under control JCTL, SFLPAG(5)/SFLSIZ(20), ROLLUP(25)/ROLLDOWN(26)) per line. Each enquiry clears and reloads the subfile, so switching from a 3-line to a 2-line document leaves exactly 2 rows; an unknown document shows Journal document not found. with an empty subfile.

Journal Inquiry - LEDGER/i Document: JE000004 Description . : CUSTOMER CASH RECEIPTS Debits . . . . : 12,000.00 Credits . . . : 12,000.00 Seq Account D Amount Memo 1 100000 D 12,000.00 CASH RECEIVED 2 110000 C 11,500.00 AR SETTLEMENT 3 400000 C 500.00 CASH SALE F3=Exit Roll=Page Enter=Inquire
Subfile field (JSFL)Type (DDS)Shows
SSEQ2Y,0 outputLine sequence (LSEQ).
SACCT6A outputAccount number.
SDC1A outputSide: D debit, C credit.
SAMT14A outputLine magnitude, edit-code K.
SMEMO22A outputLine memo.
The subfile-load order is deliberately not the naive one: the loop EXFMTs first to collect the keyed document number, THEN clears and loads that document's lines, so the next EXFMT shows what was just loaded rather than the previous enquiry's rows for one turn. The program's first EXFMT therefore shows an empty subfile, which is correct — nothing has been asked for yet. See F.3 for the DDS conditioning-indicator note that makes SFLCLR actually fire on this emulator.

B.3 Controls & audit workflow (no maker–checker, no online posting)

Honest statement: LEDGER/i does not model a four-eyes maker–checker workflow, and the online screens are inquiry-onlyGLACCTIQ and GLJRNIQ read and display; neither posts, edits or authorises anything. Journals are captured and posted by the batch chain, not from a screen. The control model the application does have is enforced in the edit/post data layer:

In sum, the control posture is batch-level validation + state gating + a durable, uniquely-keyed audit ledger, all enforced in the edit/post layer, rather than a segregation-of-duties approval workflow or an online-entry authorisation gate.

C. Batch Jobs & the Periodic Cycle ↑ top

LEDGER/i runs as four cycles rather than one monolithic job: a daily capture/edit/post/prove chain, a weekly recurring-and-trial-balance chain, a monthly reverse/close/publish/statement chain, and an annual retained-earnings roll. Each cycle is a CL program that ADDLIBLE LIB(LEDGER) and CALLs its steps in order; each takes no parameters. A scheduled run is a bare SBMJOB.

-- one-time build, then the daily cycle
CALL PGM(LEDGER/GLSETUP)
CALL PGM(LEDGER/GLREFLD)               seed chart / periods / templates / statement layout
SBMJOB CMD(CALL PGM(LEDGER/GLDAILY)) JOB(GLDAILY)

C.1 Full batch program set

ProgramCyclePurposeReadsWrites / banner
GLREFLDsetupSeed reference data.GLACCT(13), GLPERD(4), GLRECR(4), GLFSLN(7); ACCTS=13 PERIODS=4 TMPL=4 FSLINES=7.
GLJEGENdailyCapture the seeded manual journal batch; recompute batch control totals.GLBAT, GLJHDR, GLJDTL (JSTAT=E); GLJEGEN DOCS= SKIP=.
GLEDITdailyValidate each document (DB/AC/NP/PC); reject any batch with a failure.GLJHDR, GLJDTL, GLACCT, GLPERD, GLBATJSTAT V/X, BSTAT V/R; GLEDIT VALID= REJECT=.
GLPOSTdaily/weeklyPost V documents: ledger rows + ACBAL + period balances; flip to P.GLJHDR, GLJDTL, GLACCTGLLEDG, GLBAL, GLACCT; GLPOST DOCS= LINES= SKIP=.
GLBALCKdaily/annualRe-derive ledger control totals; prove DR=CR and NET=0.GLLEDGGLBALCK ROWS= DR= CR= NET= OK|BAD.
GLRECURweeklyGenerate recurring + accrual journals from templates (already validated).GLRECRGLJHDR/GLJDTL (JSTAT=V); GLRECUR GENERATED= SKIP=.
GLTRIALweekly/monthlyRe-derive GLBAL for the period straight from the detail ledger.GLLEDG, GLBALGLBAL rewritten; GLTRIAL ROWS= DR= CR=.
GLTBPRweeklyPrint the trial balance (normal-side), totals and the DR-CR difference.GLBAL, GLACCTGLTBP print; GLTBPR LINES= DR= CR= DIFF=.
GLREVACCmonthlyReverse each posted accrual (JSRC=A) into the following period.GLJHDR, GLJDTL, GLACCTGLLEDG (GSRC=V); GLREVACC DOCS= LINES= SKIP=.
GLCLOSEmonthlyProve period balances, compute net income, write close log, lock period.GLPERD, GLBAL, GLACCTGLCLOG, GLPERD(PSTAT=C); GLCLOSE PER= DR= CR= NETINC= ST=.
GLSUMPBmonthlyPublish (restate) the closed period's account summary to the SQL extract.GLBAL, GLACCTGLSUMM; GLSUMPB ROWS= DR= CR=.
GLFINSTmonthly/annualCOBOL: P&L + balance sheet from the ledger; assert assets=liab+equity+NI.GLLEDG, GLACCTGLFINST ASSETS/LIABS/EQUITY/REVENUE/EXPENSE/NETINC/BSCHECK/LEDROWS.
GLYRCLSannualClose every R/E account to zero; post the year result to retained earnings 310000.GLACCTGLLEDG (GSRC=Y), GLACCT zeroed; GLYRCLS CLOSED= NETINC= SKIP=.
GLNEWYRannualBuild the FY2027 12-period calendar; carry balance-sheet accounts forward.GLPERD, GLACCT, GLBALGLPERD (FY2027), GLBAL (POPEN); GLNEWYR YEAR= PERIODS= CARRIED=.

C.2 Daily / weekly / monthly / annual detail

Daily — GLDAILY (GLJEGEN → GLEDIT → GLPOST → GLBALCK)

The seeded worked example is five documents into batch 100001: four balanced (JE000001..JE000004) and one deliberately out of balance (JE000005, DR 1200.00 vs CR 1000.00). The edit run validates the four and rejects the fifth; the post writes 9 ledger lines (2+2+2+3), moves the account and period balances, and the balance-check proves the books.

Expected banners:
  GLJEGEN DOCS= 5 SKIP= 0
  GLEDIT VALID= 4 REJECT= 1
  GLPOST DOCS= 4 LINES= 9 SKIP= 0
  GLBALCK ROWS= 9 DR= 38500.00 CR= 38500.00 NET= 0.00 OK

Weekly — GLWEEK (GLRECUR → GLPOST → GLTRIAL → GLTBPR)

GLRECUR generates two documents into period 1 from the seeded templates: recurring JR100101 (DR 610000 100.00 / CR 150000 100.00) and accrual JA200101 (DR 600000 750.00 / CR 210000 750.00, flagged for reversal). Both enter at JSTAT='V' because they were built from a controlled template, so GLPOST picks them up (+4 ledger rows → 13). GLTRIAL then re-derives GLBAL for the period straight from the detail ledger (zeroing the period's rows first so a stale account cannot survive), which is exactly what catches a balance file that has drifted from the detail. GLTBPR prints it with a DR-CR difference of zero.

Expected banners:
  GLRECUR GENERATED= 2 SKIP= 0
  GLTRIAL ROWS= 9 DR= 39350.00 CR= 39350.00       38500 + 100 + 750
  GLTBPR LINES= ... DR= 39350.00 CR= 39350.00 DIFF= 0.00

Monthly — GLMONTH (GLREVACC → GLTRIAL → GLCLOSE → GLSUMPB → GLFINST)

GLREVACC reverses each posted accrual document (JSRC='A') by writing opposite-sign ledger rows into the following period (+2 rows → 15) — the standard accrue-and-reverse mechanic so the real invoice, when it arrives, is not double-counted. GLCLOSE accumulates the period from GLBAL, proves SUM(PDR)=SUM(PCR), computes net income (revenue − expenses, both restated positive), writes a GLCLOG row, and locks the period to PSTAT='C' only if it balanced (CSTAT='C'). GLSUMPB restates the period into GLSUMM (DELETE-then-INSERT, continuing SUMSEQ past other periods). GLFINST (COBOL) emits the statements.

Expected banners (period 1 close):
  GLREVACC DOCS= 1 LINES= 2 SKIP= ...
  GLCLOSE PER= 1 DR= 39350.00 CR= 39350.00 NETINC= 3150.00 ST= C
  GLSUMPB ROWS= ... DR= 39350.00 CR= 39350.00
  GLFINST REVENUE   15500.00
  GLFINST EXPENSE   11600.00     life-to-date: 9000 COGS + 2500 rent + 100 insurance
  GLFINST NETINC    3900.00      life-to-date; period-1-only net income is 3150.00
  GLFINST BSCHECK   0.00         assets+liabs+equity+rev+exp = 0
Two "net income" figures are correct and not in conflict: GLCLOSE reports period-1 net income of 3150.00 (from period-1 rows only), while GLFINST reports life-to-date net income of 3900.00 (across every posted period, including the period-2 rent reversal that removes the 750.00 accrual). Know which one you are reading.

Annual — GLYEAR (GLYRCLS → GLNEWYR → GLBALCK → GLFINST)

GLYRCLS writes, for each revenue/expense account, a closing ledger entry equal to the opposite of its life-to-date ACBAL (zeroing it), then posts the net of those entries (the year's net income) to retained earnings 310000 so the whole set balances. After the roll every P&L account is zero and equity carries the result. GLNEWYR builds the FY2027 calendar (12 periods, period 1 open, the rest future) and carries only the balance-sheet accounts (A/L/Q) forward as GLBAL.POPEN for FY2027 period 1. GLBALCK and GLFINST re-prove the books.

Expected banners:
  GLYRCLS CLOSED= 4 NETINC= 3900.00 SKIP= ...
  GLNEWYR YEAR= 2027 PERIODS= 12 CARRIED= 5
  GLFINST REVENUE   0.00
  GLFINST EXPENSE   0.00
  GLFINST EQUITY    3900.00       retained earnings now carries the year result
  GLFINST BSCHECK   0.00

C.3 Ordering & dependencies

D. Data Files (data dictionary) ↑ top

All files are in library LEDGER, grounded in sources.mjs. Dates are stored as packed/zoned integers in YYYYMMDD form; money is 13P 2 (packed decimal, 2 dp); the ledger sequence LEDSEQ is 15S 0 (widened from 8S0, see F.1).

GLACCT — Chart of accounts (key ACCTNO, UNIQUE)

FieldTypeMeaning
ACCTNO6AAccount number (key), e.g. 100000.
ACDESC30AAccount description.
ATYPE1AA asset, L liability, Q equity, R revenue, E expense — decides BS vs P&L, and whether the year-end roll closes it.
NORMDC1ANormal balance side, D or C (stored, not forced — a debit-normal account can legitimately carry a credit balance).
ACBAL13P 2Life-to-date balance, debit-positive signed (credits subtract).
ACSTAT1AAccount status (A active).
ACPOST1APostable? Y postable, N header/no-post (e.g. 999000).

Seeded chart (13 accounts): 100000 CASH, 110000 A/R, 120000 INVENTORY, 150000 PREPAID INSURANCE (assets, D); 200000 A/P, 210000 ACCRUED EXPENSES (liabilities, C); 300000 COMMON STOCK, 310000 RETAINED EARNINGS, 999000 SUMMARY HEADER (equity, C; 999000 is non-postable); 400000 SALES REVENUE (revenue, C); 500000 COGS, 600000 RENT EXPENSE, 610000 INSURANCE EXPENSE (expenses, D).

GLPERD — Accounting-period calendar (key FYEAR, PERNO, UNIQUE)

FieldTypeMeaning
FYEAR / PERNO4S 0 / 2S 0Fiscal year + period number (key).
PERBEG / PEREND8S 0Inclusive period date window (YYYYMMDD).
PSTAT1AO open, C closed, F future. A journal may only post into an OPEN period.
PCLSDT8S 0Close date, set when the period is locked.

Seeded: FY2026 periods 1–3 open, period 4 future.

GLBAT — Journal-batch header (key BATNO, UNIQUE)

FieldTypeMeaning
BATNO6S 0Batch number (key), e.g. 100001.
BDESC30ABatch description.
BFYEAR / BPERNO4S 0 / 2S 0Target fiscal year / period.
BSTAT1AE entered, V validated, P posted, R rejected.
BDRTOT / BCRTOT13P 2Control totals recomputed from the detail and compared.
BDOCS5S 0Document count in the batch.
BERRCD2AError code when rejected (e.g. DB).

GLJHDR — Journal entry header (key DOCNO, UNIQUE)

FieldTypeMeaning
DOCNO8ADocument number (key) — the stable business key posting derives its ledger key from.
BATNO6S 0Owning batch.
JDESC30ADocument description.
JFYEAR / JPERNO / JDATE4S 0 / 2S 0 / 8S 0Target year / period / posting date.
JSTAT1AE entered, V validated, P posted, X rejected.
JSRC1AM manual, R recurring, A accrual, V reversal, Y year-end.
JDRTOT / JCRTOT13P 2Document debit / credit totals (recomputed by the edit run from its own lines).

GLJDTL — Journal entry lines (key DOCNO, LSEQ, UNIQUE)

FieldTypeMeaning
DOCNO / LSEQ8A / 3S 0Document + line sequence (key).
ACCTNO6AAccount the line hits.
DRCR1ASide: D or C (the sign lives here).
LAMT13P 2Line amount — ALWAYS the unsigned magnitude.
LMEMO25ALine memo.
LSTAT1ALine status (E entered).

GLLEDG — Posted GL ledger (key LEDSEQ, UNIQUE)

FieldTypeMeaning
LEDSEQ15S 0Ledger sequence (key) — derived from the stable business key; disjoint ranges per program (see F.1).
ACCTNO6AAccount.
DOCNO8ASource document.
LFYEAR / LPERNO / GDATE4S 0 / 2S 0 / 8S 0Year / period / posting date.
GDRCR1ASide D/C.
GAMT13P 2Amount, debit-positive signed (a credit is negative). SUM(GAMT) over the ledger must be zero.
GSRC1ASource class M/R/A/V/Y.
GMEMO25AMemo.

GLBAL — Period balances by account (key BFYEAR, BPERNO, ACCTNO, UNIQUE)

FieldTypeMeaning
BFYEAR / BPERNO / ACCTNO4S 0 / 2S 0 / 6AYear / period / account (key).
PDR / PCR13P 2Period gross debit / credit turnover (both unsigned).
PNET13P 2Period net, debit-positive.
POPEN13P 2Opening balance (set only by the new-year carry-forward).

GLRECR — Recurring/accrual templates (key TMPLID, TSEQ, UNIQUE)

FieldTypeMeaning
TMPLID / TSEQ6A / 3S 0Template id + line sequence (key).
TDESC30ATemplate description.
TKIND1AR recurring, A accrual (accrual is reversed next period).
ACCTNO / TDRCR / TAMT6A / 1A / 13P 2Account, side, amount for the generated line.
TREVSW1AY if the generated journal must be auto-reversed.
TSTAT1ATemplate status (A active).

Seeded: RC0001 (recurring, DR 610000 / CR 150000, 100.00, not reversed) and AC0001 (accrual, DR 600000 / CR 210000, 750.00, reversed).

GLFSLN — Financial-statement layout (key STMT, FSLINE, UNIQUE)

FieldTypeMeaning
STMT / FSLINE1A / 3S 0P profit&loss / B balance sheet, then print-order line (key).
FSDESC30ALine caption.
FSTYPE1AD detail (sum accounts of type FSATYP) / T total.
FSATYP / FSSIGN1A / 1AAccount type this detail line sums / presentation sign.

Seeded: 3 P&L lines (revenue, expenses, net income) + 4 BS lines (assets, liabilities, equity, total). This turns a chart of accounts into a formatted statement without hard-coding layout in the program.

GLCLOG — Period-close log (key CLSEQ, UNIQUE)

FieldTypeMeaning
CLSEQ8S 0Close sequence = FYEAR×100 + PERNO (key, the idempotency guard).
CFYEAR / CPERNO / CDATE4S 0 / 2S 0 / 8S 0Closed year / period / date.
CDRTOT / CCRTOT13P 2Period DR / CR proof totals.
CNETINC13P 2Period net income (revenue − expenses).
CSTAT1AC closed-clean (balanced) / E error (out of balance — period not locked).

GLSUMM — SQL summary extract (PK SUMSEQ)

DB2 table published by the monthly close for downstream consolidation: SUMSEQ, SFYEAR, SPERNO, ACCTNO, ATYPE, SDR, SCR, SNET, with index GLSUMACC (SFYEAR, SPERNO, ACCTNO). GLSUMPB restates one (year, period) per run (DELETE-then-INSERT), continuing SUMSEQ past other periods, and only totals a row into its banner when the INSERT actually succeeded (SQLCOD = 0).

Relationships & access paths

E. Operations Runbook ↑ top

E.1 Day-in-the-life

  1. First-time only: CALL LEDGER/GLSETUP then CALL LEDGER/GLREFLD to build the objects/programs and seed the chart, calendar, templates and statement layout.
  2. Confirm the target period is OPEN (PSTAT='O' for FY2026 period 1 in the seeded sample).
  3. Submit the daily cycle: SBMJOB CMD(CALL PGM(LEDGER/GLDAILY)).
  4. Post-check the banners (below).
  5. On the weekly day, submit GLWEEK; check GLRECUR GENERATED=, the trial balance and GLTBPR ... DIFF= 0.00.
  6. Answer account/journal enquiries through CALL LEDGER/GLMENU (option 1 or 2).

Pre-checks: the job's library list includes LEDGER; the seeded example targets FY2026 period 1 and is open.

Post-checks after the daily cycle:

E.2 Period-end & year-end close

  1. Confirm the period's daily runs and the weekly recurring/accrual run have all posted (the accrual must be posted before it can be reversed).
  2. Submit GLMONTH. Confirm GLREVACC DOCS= 1 LINES= 2, GLCLOSE PER= 1 ... NETINC= 3150.00 ST= C (a ST= E means the period did NOT balance and was not locked), and the SQL extract / COBOL statements below.
  3. Verify the period is locked: GLPERD period 1 now PSTAT='C' with a PCLSDT. A later journal aimed at it will be edit-rejected with 'PC'.
  4. At year-end submit GLYEAR. Confirm GLYRCLS CLOSED= 4 NETINC= 3900.00, GLNEWYR YEAR= 2027 PERIODS= 12 CARRIED= 5, and that every R/E account is now zero with retained earnings 310000 carrying -3900.00 (a credit, i.e. a profit).
Review the close log and the SQL extract:
SELECT CFYEAR, CPERNO, CDRTOT, CCRTOT, CNETINC, CSTAT FROM LEDGER.GLCLOG;
SELECT SUM(SDR), SUM(SCR), SUM(SNET) FROM LEDGER.GLSUMM;   39350.00 = 39350.00, 0
SELECT SUM(SCR) FROM LEDGER.GLSUMM WHERE ATYPE = 'R';      15500.00 revenue

E.3 Reconciling figures (balanced-GL checks)

These are the same figures the daily and cycle suites derive by hand from the five seeded documents and assert against an independent oracle — every one is checkable in production the same way. The single master check is the signed ledger sums to zero.

E.4 Failure & re-run rules

Every program DSPLYs a one-line banner; a healthy daily run ends in GLBALCK ... OK. All four cycles are safely re-runnable because every write is keyed off a stable business key and guarded.

SituationBehaviourAction
Re-run GLJEGEN / GLPOST / GLRECUR / GLREVACC / GLYRCLS / GLNEWYRStable keys + CHAIN(EN) / JSTAT guards make a second run a no-op.Safe. E.g. a second post shows DOCS= 0 LINES= 0; balances and ledger row counts are unchanged.
Batch carries an out-of-balance documentGLEDIT rejects that document ('X') and the whole batch (BSTAT='R', BERRCD='DB').Correct the entry, re-capture, re-edit. Nothing from a rejected batch reaches the ledger.
Journal aimed at a closed periodEdit-rejected with error 'PC' even if perfectly balanced.Re-open the period (or re-date the journal into an open period) and re-edit.
Journal aimed at a non-postable account (e.g. 999000)Edit-rejected with error 'NP'.Re-point the line at a postable detail account.
Period fails to balance at closeGLCLOSE writes the log row with CSTAT='E' and does not lock the period.Fix the imbalance (check GLTRIAL/GLBALCK), then close again.
Second close of the same periodRefused via the GLCLOG key; banner GLCLOSE ALREADY CLOSED PER=.No action — the period is already closed; the log stays one row.
Two documents share the last 3 body digits (e.g. JE000123 / JE100123)Historically collided on a lossy ledger key; now folded losslessly into the widened 15S0 LEDSEQ.Both post under distinct keys. See F.1.
Because every money movement is a GLLEDG row (debit-positive, source-tagged, uniquely keyed) and the incremental balances can always be re-derived from that detail by GLTRIAL, any cycle's effect is fully reconstructable and independently provable after the fact for reconciliation and recovery.

F. Developer Reference ↑ top

Fixed-form RPG (C-specs) plus /free over keyed DDS, one ILE COBOL program and one embedded-SQL RPG program. All objects are in library LEDGER, held as source-physical-file members (loaded by seed.mjs' seedLedger()).

F.1 The ledger key (LEDSEQ)

Every posting program contributes to one accumulating GLLEDG history keyed UNIQUE on LEDSEQ, derived entirely from a stable business key so a re-run regenerates the same key and the CHAIN(EN) guard refuses it rather than double-posting. Ranges are disjoint per program so keys never collide across cycles:

The 2-character document prefix is folded in via a character-set scan (%scan(ch:WCSET)) so JE000001 and SM000001 can't collide, and the full 6-digit document body is folded in losslessly. LEDSEQ was widened from 8S0 to 15S0 precisely to hold this: an 8-digit field could only keep a lossy 3-digit slice of the 6-digit body, so JE000123 and JE100123 (same prefix, same last 3 digits) collided — the second validated cleanly but its lines were silently swallowed by the idempotency guard, stuck at JSTAT='V' with no diagnostic. The widened, full-body fold makes distinct documents impossible to collide while a genuine re-post still regenerates the identical key.

F.2 Programs

GLREFLD — reference-data loader (RPGLE)
Writes the 13 accounts, 4 periods, 4 template lines and 7 statement lines. Idempotency is by the caller not re-seeding; the members are fixed.
GLJEGEN — journal capture (RPGLE)
Builds the five seeded documents under fixed DOCNOs, recomputes each document's DR/CR and the batch control totals from the detail. CHAIN(EN) guards on (DOCNO,LSEQ) for lines and DOCNO for headers make it idempotent (SKIP= counts skips).
GLEDIT — edit/validate (RPGLE)
Per 'E' document: checks period open (else 'PC'), walks the lines summing DR/CR and checking account exists+active ('AC') and postable ('NP'), then DR=CR ('DB'). Sets JSTAT V/X and recomputes JDRTOT/JCRTOT. A separate pass rejects any batch that carried a failing document.
GLPOST — post (RPGLE)
Per 'V' document: for each line writes a debit-positive GLLEDG row under the stable key (F.1), moves ACBAL, and accumulates the (year,period,account) GLBAL row (gross PDR/PCR + signed PNET); then flips the header to 'P'.
GLBALCK — balance proof (RPGLE)
Reads GLLEDG, sums positive amounts as DR and negatives as CR and totals the signed net; prints OK when DR=CR.
GLRECUR — recurring/accrual generation (RPGLE)
Control-breaks on TMPLID; each template becomes one document numbered stably from the template id + period (JR100101, JA200101), source R/A, entering at 'V'. Copies ACCTNO out to a work field before the write because GLRECR and GLJDTL share the field name (see F.3).
GLTRIAL — trial-balance re-derivation (RPGLE)
Two passes: zero the period's existing GLBAL rows, then re-accumulate straight from GLLEDG — catching any drift between the incremental balances and the detail.
GLTBPR — trial-balance print (RPGLE/PRTF)
One line per non-zero period balance in normal-side presentation (positive net → debit column, negative → credit), then totals and the DR-CR difference (must be zero). Page overflow via OFLIND(*IN90).
GLREVACC — accrual reversal (RPGLE)
Per posted JSRC='A' document: writes opposite-sign GLLEDG rows (source 'V') into jperno+1 and moves ACBAL back.
GLCLOSE — period close (RPGLE)
Accumulates the period from GLBAL, computes net income from R/E accounts, writes a GLCLOG row (CSTAT C if DR=CR else E) and locks the period (PSTAT='C') only when it balanced. Guarded by the GLCLOG key.
GLSUMPB — SQL summary publish (SQLRPGLE)
Embedded-SQL DELETE of the period then INSERT per non-zero balance, continuing SUMSEQ past other periods. Only totals a row into the banner when SQLCOD = 0 — a refused insert is not reported as published.
GLYRCLS — year-end roll (RPGLE)
Pass 1 closes each R/E account with an opposite-of-balance GLLEDG row and zeroes it; pass 2 posts the net (year income) to retained earnings 310000 so the set balances. Keyed 50000000 + account.
GLNEWYR — open new fiscal year (RPGLE)
Builds the FY2027 12-period calendar (period 1 open, rest future) and carries each non-zero balance-sheet account (A/L/Q) forward as GLBAL.POPEN for FY2027 period 1.
GLACCTIQ / GLJRNIQ / GLMENU — online (RPGLE)
Account inquiry, journal-line subfile inquiry, and the menu driver (see B.2).
GLFINST — financial statements (ILE COBOL)
Reads GLLEDG and randomly reads GLACCT by account, accumulates assets / liabilities / equity / revenue / expense (debit-positive), computes net income (0 − revenue) − expense, and the balance-sheet check assets+liabs+equity+rev+exp which must be zero.

F.3 Engine-compatibility accommodations (documented in source)

F.4 Error codes & banners

Code / bannerRaised byMeaning
DBGLEDIT / GLBATDocument debits ≠ credits (and the batch rejected with it).
ACGLEDITAccount does not exist, or is not active.
NPGLEDITAccount is not postable (header account).
PCGLEDITTarget period is not open (future or closed).
CSTAT=EGLCLOSEPeriod did not balance; period NOT locked.
ALREADY CLOSEDGLCLOSEPeriod already has a close-log row; refused.
... OK / ... BADGLBALCKLedger DR=CR (OK) or not (BAD).
BSCHECK 0.00GLFINSTBalance-sheet identity holds (assets = liab + equity + NI).

G. Glossary ↑ top

Accrual (accrue-and-reverse)
An expense recognised at period end (DR expense / CR accrued liability) and auto-reversed the following period so the real invoice, when it arrives, is not double-counted. Here template AC0001, reversed by GLREVACC into jperno+1.
Batch (journal batch)
The unit of work an operator submits — a set of documents with control totals (GLBAT). One failing document rejects the whole batch.
Chart of accounts
The account master (GLACCT), keyed by ACCTNO, typed A/L/Q/R/E with a normal side D/C.
Control totals
The batch DR/CR totals (BDRTOT/BCRTOT) recomputed from the detail and compared — the classic batch-integrity check.
Debit-positive
The sign convention for stored balances and the ledger: debits positive, credits negative, so the whole ledger sums to zero when it balances.
Double entry
Every document has SUM(DR) = SUM(CR); over the whole ledger SUM(GAMT) = 0. The app's master invariant.
Idempotent
Safe to run again with the same result. Every posting program derives a stable ledger/log key and guards it with CHAIN(EN), so a re-run posts nothing.
Net income
Revenue − expenses, both restated positive from debit-positive storage. Period net income (GLCLOSE) and life-to-date net income (GLFINST) can differ — know which you're reading.
Normal side
The side an account normally carries (NORMDC). Debit-normal for assets/expenses, credit-normal for liabilities/equity/revenue. The online inquiry flips stored debit-positive balances into normal-side presentation.
Period close
Proving a period balances, recording net income, and locking it (PSTAT='C') so no further journal can post into it. GLCLOSE; logged in GLCLOG.
Posting
Turning validated journal lines into durable GLLEDG rows and moving the account/period balances. GLPOST.
Recurring journal
A fixed journal repeated every period (e.g. straight-line prepaid amortisation), template RC0001, generated by GLRECUR.
Retained-earnings roll (year-end close)
Closing every revenue/expense account to zero into retained earnings, then opening the new fiscal year with carried-forward balance-sheet balances. GLYRCLS + GLNEWYR.
SBMJOB
Submit Job — the IBM i command that queues a program to run as a batch job (e.g. SBMJOB CMD(CALL PGM(LEDGER/GLDAILY))).
Subfile
A 5250 display construct listing many rows on one screen (DDS SFL/SFLCTL). GLJRNIQ's line list is a subfile, cleared and reloaded per enquiry.
Trial balance
Every account's period turnover/net, re-derived from the detail ledger (GLTRIAL) and printed (GLTBPR) with a DR-CR difference that must be zero.