DEPOSIT/i — Core Banking / Demand Deposit

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

DEPOSIT/i is an AS/400-native core banking / demand-deposit (DDA) system: customer and account masters, teller postings (deposit / withdrawal / transfer) against a running-balance ledger, holds that reduce available balance, tiered daily interest accrual with month-end crediting, service charges plus dormancy flagging, overdraft-limited withdrawals, a balanced double-entry general-ledger feed, and passbook/trial-balance print. It is a classic RPG IV + ILE COBOL + DDS + embedded-SQL application: the business math lives in packed-decimal RPG that reads and updates keyed physical files and appends to a journaled ledger, while the GL feed and its cross-checks run through EXEC SQL against a DB2 table. This manual is the reference for the operator who runs the teller line, the online inquiry screens and the nightly batch chain, and for the developer maintaining the application. It is grounded entirely in the committed source (banking-app/src/sources.mjs, src/seed.mjs, and the test/dep_*.mjs drivers).

Contents

A. Overview & Architecture ↑ top

A.1 What it does

DEPOSIT/i runs a single line of business — retail demand-deposit banking (savings + checking):

A.2 A single RPG/COBOL layer over keyed files + a DB2 GL feed

Unlike a stored-procedure design, DEPOSIT/i keeps all business logic in the application programs themselves. There are two data substrates with a clear division of use:

The consequence for operations: money movement is native-file and immediate (and journaled for audit), while the general ledger — the place a balanced control total is proven — is SQL, and is the only substrate under commitment control (see F.3). Everything runs in library DEPOSIT.

A.3 Component & flow

  MAINTENANCE           ONLINE (5250)          BATCH (night chain / on demand)
  -----------           -------------          ------------------------------
  ACCTMNP --READ-->     TELMENUP (menu)        INTACCR  --> ACCTMST.ACCRINT (tiered daily)
    ACCTMNT               opt 1 -> ACCTINQ     INTCRED  --> ACCRINT into CURBAL + 'I' TXN
    (O/C/R)              (plain inquiry)        SVCCHG   --> 'C' fee TXN + dormant flag
                         opt 2 -> REGINQ        GLPOST   --> GLLEDGER DR/CR (SQL, balanced)
  TXNPEND --READ-->     (SFL register)          TRIALRPT --> TRIALPR trial balance (DR=CR)
    TELLER                                       STMTGEN  --> STMTPR passbook/statement
    (D/W/X)                                      DDABAL   --> COBOL COMP-3 total vs GL SUM
       |     \                                  /
       |      \                                /
       v       v                              v
   ACCTMST  <--update-- HOLDPOST (AVLBAL = CURBAL - open holds)
       |
       +--> TXNJRNL (append-only, journaled to TXNJRN; running balance per row)
                |
                +--> TXNLF (keyed by account) --> REGINQ subfile + STMTGEN register

A single teller posting flows: an op is written to TXNPENDTELLER reads it, CHAINs ACCTMST, validates available balance (+ overdraft), updates CURBAL/AVLBAL, and WRITEs a TXNJRNL row carrying the running balance → the journal captures the ledger WRITE. A transfer is two such postings (a TRANSFER OUT debit leg and a TRANSFER IN credit leg).

A.4 Object inventory

ObjectTypeRole
CUSTMSTPFCustomer master.
ACCTMSTPFAccount master (the heart of the app).
TXNJRNLPF (journaled)Teller transaction ledger (append-only).
TXNLFLFLedger keyed by account then txn id (register/passbook).
HOLDMSTPFHolds against available balance.
HOLDLFLFHolds keyed by account.
INTPLANPFInterest rate tiers (product + balance band).
CHGPLANPFService-charge plans.
TXNPENDPFPending teller-op work file.
ACCTMNPPFPending account-maintenance work file.
GLLEDGERDB2 tableDouble-entry GL feed (SQL DDL).
ACCTDSPF / REGDSPF / TELMENUDSPFInquiry / subfile register / menu displays.
STMTPR / TRIALPRPRTFStatement/passbook & trial-balance printer files.
SEEDDATARPGLEData-seed loader (customers/accounts/tiers/plans/hold).
ACCTMNTRPGLEAccount master maintenance (open/change/re-rate).
HOLDPOSTRPGLERecompute available balance from open holds.
TELLERRPGLE + SQLPost deposits/withdrawals/transfers.
INTACCRRPGLEDaily tiered interest accrual (packed-decimal).
INTCREDRPGLE + SQLMonth-end interest crediting.
SVCCHGRPGLE + SQLService charges + dormancy flagging.
GLPOSTRPGLE + SQLBalanced DR/CR GL feed + control total.
STMTGENRPGLEPassbook/statement PRTF register.
TRIALRPTRPGLE + SQLGL trial-balance PRTF (DR=CR).
ACCTINQRPGLEPlain WORKSTN account inquiry.
REGINQRPGLESFL/SFLCTL account-register subfile.
TELMENUPRPGLEMenu program routing to the two inquiries.
DDABALILE COBOL + SQLCOMP-3 ledger total vs GL SUM cross-check.
DBSETUP / DBNIGHTCLBuild-everything driver / night-batch chain.

The catalogue is 8 PFs + 2 LFs + 1 DB2 table, 3 DSPFs + 2 PRTFs, driven by 13 RPG programs + 1 ILE COBOL program + 2 CL drivers. Sections D and F expand each.

B. Online Transactions & Screens ↑ top

B.1 The command/entry line

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

To do thisType on the command line
Open the teller menu (routes to the two inquiries)CALL DEPOSIT/TELMENUP
Open the plain account inquiry directlyCALL DEPOSIT/ACCTINQ
Open the account register (subfile) directlyCALL DEPOSIT/REGINQ
Apply pending account maintenanceCALL DEPOSIT/ACCTMNT
Post the pending teller opsCALL DEPOSIT/TELLER (or SBMJOB it)
Recompute available balances from holdsCALL DEPOSIT/HOLDPOST
Build/rebuild the whole app + seedCALL DEPOSIT/DBSETUP
Submit the night batch chainCALL DEPOSIT/DBNIGHT

The maintenance/teller/batch programs take no CALL parameters — each reads its work from a pending file (ACCTMNP, TXNPEND) or processes every account, and DSPLYs a one-line result. There is no interactive posting screen: postings and maintenance are driven from the pending work files (the classic AS/400 work-file → poster idiom), and the interactive programs (TELMENUP, ACCTINQ, REGINQ) are inquiry only.

B.2 The menu & inquiry screens (TELMENUP / ACCTINQ / REGINQ)

Three WORKSTN programs make up the online tier. TELMENUP displays a two-option menu (TELMENU / record MENUFMT) and dynamically CALLs ACCTINQ for option 1 or REGINQ for option 2. Both inquiry programs loop on EXFMT until F3 (CA03), and both look an account up by CHAINing ACCTMST then CUSTMST for the customer name.

The teller menu (TELMENUP / TELMENU)

DEPOSIT/i Teller Menu 1. Account Inquiry 2. Account Register Option . . . . : _ F3=Exit Enter=Select

Option 1 sets indicator 81 and calls ACCTINQ; option 2 sets 82 and calls REGINQ; anything else shows Invalid option. (The fixed-form CALLs are indicator-conditioned in cols 9–11 — see quirk PQ-1.)

Plain account inquiry (ACCTINQ / ACCTDSPF)

A single, non-subfile display (ACCTINQ record ACCTINQF). The operator keys an account number in IACCTNO; on Enter the program chains the master and shows the customer name, product (SAVINGS / CHECKING from PRODCD), status (ACTIVE / DORMANT / CLOSED from ASTAT), and the current / available / accrued figures. An unknown account yields Account not found: nnnnnnnnnn.

Account Inquiry - DEPOSIT/i Account number: ACS0000001 Customer . . . : ADAMS RILEY Product . . . : SAVINGS Status . . . . : ACTIVE Current bal . : 5000.00 Available bal : 5000.00 Accrued int . : 0.0000 Account found. F3=Exit Enter=Inquire

Account register subfile (REGINQ / REGDSPF)

REGINQ is the app's one subfile screen (DDS record REGSFL under control record REGCTL, SFLPAG(0012) per page, SFLSIZ(0030)). The operator keys an account in IREACCT; the program clears the subfile (SFLCLR, indicator 33), then loops the ledger logical TXNLF keyed by that account (SETLL / READE), WRITEing one REGSFL row per transaction, and EXFMTs the register — a real "clear, load, display" subfile pass, reloaded each time an account is inquired.

Account Register - DEPOSIT/i Account number: ACC0000002 Customer . . . : ADAMS RILEY Current bal . : 6000.00 Date Ty Amount Balance Description 20260115 W 200.00 1000.00 TELLER WITHDRAWAL 20260115 X 5000.00 6000.00 TRANSFER IN F3=Exit Enter=Inquire

Subfile columns (REGSFL)

FieldType (DDS)Shows
SXDT8A outputTransaction date (from TXNDT).
SXTYP1A outputType: D deposit, W withdrawal, X transfer, I interest, C charge.
SXAMT14A outputTransaction amount (from TXNAMT).
SXBAL14A outputRunning balance after this posting (from RUNBAL).
SXDESC20A outputPosting description (from DESCR).
The register subfile is display-only — there is no option column and no action against a row; it renders the account's passbook/register. Balance-changing actions happen only through the poster programs (TELLER, the batch jobs). The load indicators mirror the DDS numbering: 31 SFLDSP, 32 SFLDSPCTL, 33 SFLCLR, 34 SFLEND(*MORE).

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

Honest statement: DEPOSIT/i does not model a true four-eyes maker–checker / separate-authorization workflow. There is no "one user posts, a second user approves" step in the code: a teller op written to TXNPEND is applied immediately when TELLER runs, and a maintenance action in ACCTMNP is applied immediately by ACCTMNT. The manual documents the control model the application does have:

In sum, the control posture is journaled audit + validation/status gating + a balanced-GL control total + atomic (SQL) transfers, rather than a segregation-of-duties approval workflow.

C. Batch Jobs & the Periodic Cycle ↑ top

DEPOSIT/i's processing is a set of poster and periodic programs rather than one monolithic nightly job: on-demand posters (ACCTMNT maintenance, TELLER teller line, HOLDPOST), a daily accrual (INTACCR), and a month-end / night chain (INTCREDSVCCHGGLPOST) plus reporting (STMTGEN, TRIALRPT, DDABAL). None take CALL parameters. The night chain is packaged in the CL driver DBNIGHT, which SBMJOBs each step onto its own night job queue so each step's writes are visible to the next (see quirk PQ-3).

-- DBNIGHT: create the night queue, then submit each step onto it
CRTJOBQ JOBQ(DEPOSIT/NITEQ) TEXT('Deposit night batch')
SBMJOB CMD(CALL DEPOSIT/INTACCR) JOB(DBACCR) JOBQ(DEPOSIT/NITEQ) HOLD(*NO)
SBMJOB CMD(CALL DEPOSIT/INTCRED) JOB(DBCRED) JOBQ(DEPOSIT/NITEQ) HOLD(*NO)
SBMJOB CMD(CALL DEPOSIT/SVCCHG)  JOB(DBSVC)  JOBQ(DEPOSIT/NITEQ) HOLD(*NO)
SBMJOB CMD(CALL DEPOSIT/GLPOST)  JOB(DBGL)   JOBQ(DEPOSIT/NITEQ) HOLD(*NO)

C.1 Full batch program set

ProgramPurposeReads / writesOutput (DSPLY)Frequency
ACCTMNT Apply pending account maintenance: Open / Change status / Re-rate. Reads ACCTMNP; writes/updates ACCTMST. ACCTMNT APPLIED=n REJECT=m On demand.
HOLDPOST Recompute available balance = current − sum of open holds. Reads HOLDLF; updates ACCTMST.AVLBAL. HOLDPOST: available recomputed for n accounts After hold changes / in DBSETUP.
TELLER Post deposits / withdrawals / transfers from the pending work file. Reads TXNPEND; updates ACCTMST; writes TXNJRNL. TELLER POSTED=n REJECT=m On demand (teller line).
INTACCR Accrue one day of tiered interest into ACCRINT for each active account. Reads INTPLAN; updates ACCTMST.ACCRINT. INTACCR: accrued interest for n accounts Daily.
INTCRED Move accrued interest into the balance, zero ACCRINT, post an 'I' ledger row. Updates ACCTMST; writes TXNJRNL (TXNID via MAX+1). INTCRED: credited n accts total t Month-end.
SVCCHG Post monthly maintenance fees (unless waived) and flag dormant accounts. Reads CHGPLAN; updates ACCTMST; writes TXNJRNL ('C'). SVCCHG: fees=n dormant=m Month-end.
GLPOST Consolidate ledger activity into balanced DR/CR GL rows; cross-check DR=CR. Reads TXNJRNL; EXEC SQL INSERT into GLLEDGER. GLPOST BATCH b IN BALANCE DR=d (or OUT OF BALANCE) Night / month-end.
TRIALRPT Print the GL trial balance from GLLEDGER (SQL cursor), prove DR=CR. Reads GLLEDGER; spools TRIALPR. TRIALRPT rows=n DR=d CR=c Night / month-end.
STMTGEN Print each account's statement/passbook register (running balance). Reads ACCTMST/CUSTMST/TXNLF; spools STMTPR. STMTGEN: printed n register lines Statement cycle.
DDABAL ILE COBOL: total ledger balance by product (COMP-3 FD), cross-check GL deposit liability. Reads ACCTMST; EXEC SQL SUM over GLLEDGER. DDABAL SAVINGS.. CHECKING.. TOTAL.. GL NET DEPOSIT LIABILITY.. Reconciliation.

C.2 Teller / daily / month-end detail

Teller — TELLER

Reads the current high-water TXNID once at entry (EXEC SQL SELECT COALESCE(MAX(TXNID),0)) and advances a local counter per WRITE (quirk PQ-3: a program's own native WRITEs are not visible to its same-program embedded SQL, so per-row MAX+1 would collide on the UNIQUE key). Then per pending op: deposit credits CURBAL+AVLBAL and writes a 'D' row; withdrawal is rejected if PAMT > AVLBAL + ODLIM, else debits and writes 'W'; transfer debits the FROM account (writes TRANSFER OUT) then credits the TO account (writes TRANSFER IN) — two ledger rows, one balanced double-entry.

Expected DSPLY (dep + wd + transfer(2 legs) + dep, with 2 overdraft rejects):
  TELLER POSTED=5 REJECT=2   5 ledger rows written, 2 ops rejected
Reconciling example from dep_daycycle: seed ACS0000001 5000, deposits 1500 + 75 → 6575.00; ACC0000002 1200 − 200 (wd) + 5000 (transfer-in) → 6000.00; ACS0000003 25000 − 5000 (transfer-out) → 20000.00; ACC0000004 stays 300.00 (both its ops overdraft-rejected). The transfer's OUT leg amount == IN leg amount == 5000.00.

Daily — INTACCR

For each ASTAT='A' account: pick band B when CURBAL ≥ 10000.00 else band A, CHAIN (PRODCD:BAND) into INTPLAN (falling back to band A if the tier is missing), and if a tier is found with FACTOR > 0, accrue one day's simple interest ACCRINT += CURBAL × (FACTOR / 365) in packed-decimal math and UPDATE. Savings earns; the 0%-rate checking product accrues nothing. INTACCR posts no ledger row (it only grows the ACCRINT bucket).

Expected packed-decimal accrual (band A, 5000 @ 2.5% APY, one day):
  ACS0000001 ACCRINT ~= 5000 * 0.025 / 365 = 0.3425   (11P4, < $1/day)
  ACS0000003 (25000, band B @ 3.5%) accrues MORE than band A
  ACC0000002 (checking, 0%) accrues 0

Month-end — INTCRED, then SVCCHG, then GLPOST

INTCRED: for each active account with ACCRINT ≥ 0.01, add the accrued interest to CURBAL+AVLBAL, zero ACCRINT, and write an 'I' INTEREST CREDIT ledger row (TXNID from MAX+1 advanced locally). SVCCHG: for each active account, CHAIN its CHGPLAN; if the fee is positive and CURBAL < WAIVBAL, debit the MFEE and write a 'C' SERVICE CHARGE row; then, independently, if LSTACT < cutoff (20251001), set ASTAT='D' (dormant). GLPOST: sum the ledger by type, then post the double-entry legs (deposits DR 1000-CASH / CR 2000-DEPL; withdrawals reverse; interest DR 5000-INTX / CR 2000-DEPL; fees DR 2000-DEPL / CR 4000-FEEI; transfers net within 2000-DEPL), then cross-check the batch DR total equals its CR total.

Expected DSPLY (month-end chain):
  INTCRED: credited 2 accts total 0.68
  SVCCHG: fees=1 dormant=1        ACC0000004: 300 - 8 fee = 292, then flagged D
  GLPOST BATCH 1 IN BALANCE DR=...  batch DR total = CR total
SVCCHG updates the same account twice in one pass (the fee UPDATE and the dormancy-flag UPDATE); the program re-CHAINs for a fresh read between them so both persist (quirk PQ-9 / PHASE22-2 avoidance). A regression would silently drop the dormancy flag.

C.3 Ordering & dependencies

D. Data Files (data dictionary) ↑ top

All files are in library DEPOSIT, grounded in the DDS/SQL members in src/sources.mjs. Money is packed decimal (11P 2); accrued interest is 11P 4; interest rates are 5P 4 (0.0250 = 2.50% APY) and tier factors 7P 5; dates are stored as zoned 8S 0 in YYYYMMDD form.

CUSTMST — Customer master (K CUSTNO, UNIQUE)

FieldTypeMeaning
CUSTNO6ACustomer number (key).
CNAME25ACustomer name.
ADDR / CITY / ST / ZIP20A / 15A / 2A / 5AAddress components.
OPNDT8S 0Customer open date (YYYYMMDD).

ACCTMST — Account master (K ACCTNO, UNIQUE)

FieldTypeMeaning
ACCTNO10AAccount number (key), e.g. ACS0000001 (S) / ACC0000002 (C).
ACUSTNO6AOwning customer (→ CUSTMST).
PRODCD1AProduct: S savings, C checking/DDA.
ASTAT1AStatus: A active, D dormant, X closed.
CURBAL11P 2Current (ledger) balance.
AVLBAL11P 2Available balance = current − open holds.
ACCRINT11P 4Accrued-unpaid interest bucket (4dp).
INTRATE5P 4Account interest rate (0.0250 = 2.50% APY).
ODLIM9P 2Overdraft limit (extends withdrawable available).
CHGCD2AService-charge plan code (→ CHGPLAN).
AOPNDT / LSTACT8S 0Account open date / last-activity date (drives dormancy).

TXNJRNL — Teller transaction ledger (K TXNID, UNIQUE, journaled)

FieldTypeMeaning
TXNID9S 0Arrival-sequence transaction id (key).
TACCTNO10AAccount posted to.
TXNTYPE1AD deposit, W withdrawal, X transfer, I interest, C charge.
TXNDT8S 0Transaction date (YYYYMMDD).
TXNAMT11P 2Transaction amount.
RUNBAL11P 2Running balance AFTER this posting (passbook idiom).
DESCR20APosting description, e.g. TRANSFER IN.

Logical TXNLF (PFILE(TXNJRNL)) keys the same rows by TACCTNO then TXNID — the passbook/register access path used by REGINQ and STMTGEN.

HOLDMST — Holds (K HACCTNO, HLDSEQ, UNIQUE)

FieldTypeMeaning
HACCTNO / HLDSEQ10A / 2S 0Account + hold sequence (composite key).
HLDAMT11P 2Held amount (reduces available when open).
HLDEXP8S 0Hold expiry date.
HSTAT1AHold status: O open (counted by HOLDPOST).
HDESC20AHold description, e.g. LEGAL HOLD.

Logical HOLDLF (PFILE(HOLDMST)) keyed by HACCTNO — the per-account access path HOLDPOST sums.

INTPLAN — Interest rate tiers (K PRODCD, BAND, UNIQUE)

FieldTypeMeaning
PRODCD / BAND1A / 1AProduct + band (A base, B high-balance) (key).
MINBAL11P 2Minimum balance for the band.
FACTOR7P 5Annual rate (0.02500 = 2.5% APY); daily rate = FACTOR / 365.

Seeded tiers: S/A 0→0.02500, S/B 10000→0.03500, C/A 0→0.00000 (checking earns nothing).

CHGPLAN — Service-charge plans (K CHGCD, UNIQUE)

FieldTypeMeaning
CHGCD2ACharge plan code (key), matched from ACCTMST.CHGCD.
MFEE7P 2Monthly maintenance fee.
WAIVBAL11P 2Minimum balance that waives the fee.
CDESCR20APlan description.

Seeded plans: S1 fee 2.00 / waive 1000.00; C1 fee 8.00 / waive 2500.00.

TXNPEND — Pending teller-op work file (arrival sequence)

FieldTypeMeaning
PSEQ5S 0Arrival sequence.
PTYP1AOp type: D deposit, W withdrawal, X transfer.
PFRM / PTO10A / 10AFrom/primary account; to account (transfer only).
PAMT11P 2Amount to post.

ACCTMNP — Pending account-maintenance work file (arrival sequence)

FieldTypeMeaning
MSEQ5S 0Arrival sequence.
MTYP1AAction: O open, C change status, R re-rate.
MACCT10AAccount being maintained.
MCUST / MPROD / MCHG6A / 1A / 2ACustomer / product / charge-plan (open only).
MNSTAT1ANew status A/D/X (change only).
MRATE5P 4New interest rate (re-rate).
MBAL11P 2Opening balance (open only).

GLLEDGER — General-ledger feed (DB2 table)

FieldTypeMeaning
BATCHIDINTEGERGL batch number (from MAX(BATCHID)+1).
ACCTCHAR(10)GL account: 1000-CASH, 2000-DEPL, 4000-FEEI, 5000-INTX.
DRCRCHAR(1)D debit / C credit.
AMTDECIMAL(13,2)Leg amount.
SRCPGMCHAR(10)Source program (e.g. GLPOST).

Relationships

E. Operations Runbook ↑ top

E.1 Day-in-the-life

  1. Apply any account maintenance: load actions into ACCTMNP, then CALL DEPOSIT/ACCTMNT; confirm ACCTMNT APPLIED=n REJECT=m.
  2. If holds changed, CALL DEPOSIT/HOLDPOST to recompute available balances.
  3. Post the teller line: load ops into TXNPEND, then CALL DEPOSIT/TELLER (or SBMJOB it); confirm TELLER POSTED=n REJECT=m.
  4. Handle inquiries interactively through CALL DEPOSIT/TELMENUP (option 1 = account inquiry, option 2 = register).
  5. Run the daily accrual: SBMJOB CMD(CALL DEPOSIT/INTACCR).
  6. Print statements/trial balance as needed: CALL DEPOSIT/STMTGEN, CALL DEPOSIT/TRIALRPT.

Pre-checks: confirm the job's library list includes DEPOSIT; confirm the pending work files (TXNPEND/ACCTMNP) hold the intended rows before running the poster.

Post-checks after teller/accrual:

E.2 Month-end close

  1. Confirm the month's daily INTACCR runs are complete (accrued interest is up to date).
  2. Submit the night chain: CALL DEPOSIT/DBNIGHT (or the four SBMJOBs manually), which runs INTCRED → SVCCHG → GLPOST in order on DEPOSIT/NITEQ.
  3. Confirm each step: INTCRED: credited n accts, SVCCHG: fees=n dormant=m, GLPOST BATCH b IN BALANCE.
  4. Prove and reconcile the GL:
SELECT SUM(CASE WHEN DRCR='D' THEN AMT ELSE 0 END) AS DR,
       SUM(CASE WHEN DRCR='C' THEN AMT ELSE 0 END) AS CR
  FROM DEPOSIT.GLLEDGER;

Reconciling figures (the same ones the day-cycle and interest suites assert):

Statement cycle: CALL DEPOSIT/STMTGEN spools one STMTPR passbook register per account (one DETAIL line per ledger txn, an END OF ACCOUNT total line, paginated past the 66-line page via OVRFLW).

E.3 Failure & re-run rules

Each program DSPLYs a one-line result to the joblog; the SQL programs (GLPOST/TRIALRPT) additionally report IN BALANCE / OUT OF BALANCE. A healthy month-end ends with a balanced GL.

SituationBehaviourAction
Overdraft-rejected teller opTELLER increments REJECT=, leaves the account untouched, writes no ledger row.Correct the amount (or the overdraft limit) and re-post only the failed op — already-posted ops are consumed.
Duplicate account openACCTMNT rejects an MTYP='O' whose MACCT already exists (CHAIN before WRITE); existing master unchanged.Rejected count is expected; fix the account number to open a genuinely new one.
Re-run INTACCR same dayNot idempotent — each run accrues another day into ACCRINT.Run INTACCR exactly once per business day; a duplicate over-accrues. Correct by re-seeding / adjusting before crediting.
Re-run INTCREDCredits only accounts with ACCRINT ≥ 0.01; after crediting, ACCRINT=0 so a second run is a no-op.Naturally idempotent once accrual is zeroed — safe to re-run.
Re-run SVCCHG same monthNot idempotent — posts another fee to still-low-balance accounts.Run once per month; do not resubmit. A dormancy flag re-set is harmless (already D).
GLPOST OUT OF BALANCEBatch DR total ≠ CR total (a data / posting problem).Investigate the batch's rows in GLLEDGER WHERE BATCHID=b; the double-entry legs per type should net.
Night step fails partwayEach step is a separate SBMJOB; a later step may not have run.Re-submit the failed step; because steps are ordered on the queue, confirm each reaches OUTQ before the next (PQ-3).
DSPJRN shows Entries:0The list/double-paren FILE((lib obj)) form returns no entries (PG5-401).Use the single-paren form: DSPJRN JRN(DEPOSIT/TXNJRN) FILE(DEPOSIT/TXNJRNL).
Because every money movement is journaled to TXNJRNL (with a post-txn running balance) and the ledger is under STRJRNPF IMAGES(*BOTH), any day's effect is fully reconstructable after the fact for reconciliation and recovery; the GL transfer legs are additionally atomic on the SQL path under commitment control.

F. Developer Reference ↑ top

The complete program surface, from src/sources.mjs. All objects are in library DEPOSIT; the build/seed path is DBSETUP (CL) → seedDeposit() (src/seed.mjs) which loads each member into QDDSSRC/QRPGLESRC/ QCBLLESRC/QCLSRC/QSQLSRC.

F.1 Programs (13 RPG + 1 COBOL)

SEEDDATA (RPGLE)
Loads 3 customers, 4 accounts, 3 interest tiers, 2 charge plans and 1 legal hold; called by DBSETUP.
ACCTMNT (RPGLE)
Reads ACCTMNP; per MTYP: Open (CHAIN-guard against duplicate, then WRITE a UNIQUE-keyed master — escapes PHASE22-1), Change status, Re-rate. At most one UPDATE per read record, re-CHAINing for a fresh read (escapes PHASE22-2). Nested IF, not elseif (PQ-1).
HOLDPOST (RPGLE)
For each account, sum open HOLDLF rows and set AVLBAL = CURBAL − hsum.
TELLER (RPGLE + SQL)
Reads TXNPEND; deposit / withdrawal (overdraft-checked) / transfer (two legs). TXNID from MAX(TXNID) read once then advanced by a local counter (PQ-3). Writes TXNJRNL with running balance.
INTACCR (RPGLE)
Tiered daily accrual: band from CURBAL, CHAIN (PRODCD:BAND) INTPLAN (PQ-2), ACCRINT += CURBAL × FACTOR/365 in packed decimal.
INTCRED (RPGLE + SQL)
Month-end: move ACCRINT into balance, zero it, write an 'I' ledger row.
SVCCHG (RPGLE + SQL)
Post 'C' maintenance fee unless CURBAL ≥ WAIVBAL; independently flag ASTAT='D' when LSTACT < cutoff. Re-CHAINs between the two UPDATEs (PQ-9).
GLPOST (RPGLE + SQL)
Sum the ledger by type, insert balanced DR/CR legs into GLLEDGER, cross-check batch DR=CR.
STMTGEN (RPGLE, PRTF)
Per-account passbook/statement register over TXNLF, forcing a page overflow (OFLIND(*INOF)).
TRIALRPT (RPGLE + SQL, PRTF)
SQL cursor over GLLEDGER ordered by batch/account/DRCR; prints each row and a DR=CR total line.
ACCTINQ (RPGLE, WORKSTN)
Plain EXFMT ACCTINQF account inquiry: chain master + customer, render product/status/balances.
REGINQ (RPGLE, WORKSTN + SFL)
SFL/SFLCTL register: clear (33), load one REGSFL row per TXNLF txn by RRN, EXFMT REGCTL.
TELMENUP (RPGLE, WORKSTN)
Menu EXFMT MENUFMT; indicator-conditioned fixed-form CALL 'ACCTINQ' / CALL 'REGINQ' (PQ-1).
DDABAL (ILE COBOL + SQL)
Reads ACCTMST via a COMP-3 FD (zoned 8S 0 dates as PIC S9(8) DISPLAY, PQ-6); totals savings/checking ledger balances; EXEC SQL computes net GL deposit liability from GLLEDGER.

F.2 Embedded-SQL patterns

The RPG and COBOL programs use these embedded-SQL idioms against the DB2 layer:

F.3 Journaling & commitment control

Journaling. DBSETUP creates the receiver TXNRCV and journal TXNJRN, then STRJRNPF FILE(DEPOSIT/TXNJRNL) JRN(DEPOSIT/TXNJRN) IMAGES(*BOTH). Every RPG WRITE/UPDATE to the ledger is captured as an R/PT (record-put) entry stamped with the posting job; jrn.entries() and DSPJRN both surface them. Use the single-paren FILE(DEPOSIT/TXNJRNL) form of DSPJRN — the double-paren list form returns Entries:0 (PG5-401).

Commitment control. Honored on the SQL/RUNSQL path only, not native record I/O (PQ-5). So the transfer's atomicity is proven on the GL: STRCMTCTL LCKLVL(*CHG), two RUNSQL ... COMMIT(*CHG) INSERTs of the DR and CR legs, then ROLLBACK (row count returns to baseline — both legs discarded) or COMMIT (both persist), then ENDCMTCTL. The native TELLER two-leg transfer relies instead on the journaled ledger for reconstructability.

F.4 Platform quirk points (PQ)

The source is written to sidestep several documented emulator behaviours; these are the ones that shape the application code and matter to a maintainer:

PQBehaviourHow the app handles it
PQ-1Free-form elseif can mis-scope the trailing END-IF.Nested if/else throughout (TELLER, ACCTMNT, TELMENUP).
PQ-2Free-form CHAIN can't reference a fixed-form named KLIST.Parenthesised composite key: chain (prodcd:band) intr; (INTACCR).
PQ-3A program's own native WRITEs aren't visible to its same-program embedded SQL; SBMJOB writes visible to the next step only across jobs.Read MAX(id) once, advance a local counter; night chain uses one SBMJOB per step.
PQ-4PRTF field-position digits are column-sensitive (end at DDS col 44).STMTPR / TRIALPR field positions authored accordingly.
PQ-5Commitment control / ROLLBACK honored on the SQL path, not native record I/O.Transfer atomicity proven via RUNSQL on GLLEDGER.
PQ-6Zoned nS 0 fields map to COBOL PIC S9(n) DISPLAY, not COMP-3.DDABAL FD maps AC-OPNDT/AC-LSTACT as DISPLAY.
PQ-8*EXCLUDE + *USE-only on a batch-written file can throw SFF9802 even for a privileged user.Adopted authority: USRPRF(*OWNER) program lets a weak profile post to the *PUBLIC *EXCLUDE ledger; the same program without it is denied.
PQ-9PHASE22-2: a second UPDATE of the same read record can be dropped.SVCCHG re-CHAINs before the dormancy UPDATE so both the fee and the flag persist.

G. Glossary ↑ top

Accrual (interest accrual)
Recognising interest earned each day on the current balance, added to the ACCRINT bucket. Here: simple daily interest, 365-day convention, CURBAL × FACTOR/365 (INTACCR), in packed decimal.
Available balance
AVLBAL = current balance minus the sum of open holds (HOLDPOST). Withdrawals/transfers are limited to AVLBAL + ODLIM.
Band (rate band)
A within-product interest tier in INTPLAN: band A base, band B for high balances (CURBAL ≥ 10000). INTACCR chooses the band from the balance.
Crediting (interest crediting)
Moving the accrued-interest bucket into the balance at month-end and posting an 'I' ledger row (INTCRED), then zeroing ACCRINT.
DDA / demand deposit
A demand-deposit account (checking/savings) whose balance is payable on demand — the line of business DEPOSIT/i models.
Dormancy
Flagging an account inactive (ASTAT='D') when its last activity date predates a cutoff (SVCCHG). Dormant accounts are skipped by the interest/charge passes.
Double-entry / DR=CR
Every GL posting has equal debit and credit legs; a batch is "in balance" when SUM(DR)=SUM(CR) (GLPOST / TRIALRPT / DDABAL).
Hold
A claim (check hold / legal hold) that reduces available balance without changing the current balance (HOLDMST, applied by HOLDPOST).
Journaling
Capturing before/after images of every change to a file. The ledger TXNJRNL is journaled (STRJRNPF IMAGES(*BOTH)) to TXNJRN, viewable via DSPJRN.
Overdraft limit (ODLIM)
An amount by which a withdrawal/transfer may exceed the available balance before it is rejected.
Packed decimal (nPd)
The IBM i money/number encoding used by ACCTMST/TXNJRNL (11P 2) and the accrual math (11P 4).
Passbook / register
The per-account list of transactions with a running balance (the TXNLF access path; rendered by REGINQ and STMTGEN).
Running balance (RUNBAL)
The account balance immediately after a posting, stored on each ledger row so the register/statement reads like a passbook.
SBMJOB / *JOBQ
Submit Job onto a job queue — the night chain (DBNIGHT) submits each batch step onto DEPOSIT/NITEQ so its writes are visible to the next step.
Service charge
A monthly maintenance fee (CHGPLAN) posted as a 'C' ledger row unless the minimum-balance waiver applies (SVCCHG).
SFL / subfile
A 5250 display construct listing many rows on one screen (DDS SFL/SFLCTL). REGINQ's account register is a subfile.
Transfer (two-leg)
Moving funds between accounts as a debit leg (TRANSFER OUT) on the from-account and an equal credit leg (TRANSFER IN) on the to-account — one balanced double-entry (TELLER).
Work file (pending file)
An arrival-sequence PF (TXNPEND, ACCTMNP) holding requested actions that a poster program (TELLER, ACCTMNT) reads and applies — the classic AS/400 batch idiom.