CIVICA/i is a homeowner-association (HOA) / community-association management application:
association and unit inventory, owner records, a recurring monthly assessment (dues) charge run and
one-off special assessments, an owner receivables ledger, daily cash-receipt application, period-end
delinquency aging with late fees and a lien ladder, code-violation and fine posting, reserve-fund
transfers, and a balanced GL feed. The bulk of the application is ILE RPG driving keyed physical files
(with one COBOL AR control report and one SQLRPGLE GL post over a DB2 for i table); the three CL job
cycles orchestrate the batch programs. 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 (hoa-app/src/sources.mjs, src/seed.mjs, and
the test/cv_build.mjs / cv_cycle.mjs drivers). Everything runs in library
CIVICA.
CIVICA/i manages the receivables life of a community association:
Unlike a SQL-PL-centred application, CIVICA/i keeps its business logic in the RPG programs themselves, operating directly on keyed DDS physical files. The design has a few deliberate properties:
SETLL/READ/
READE to walk, CHAIN to fetch a keyed row, UPDATE/WRITE
to post) and codes the rules inline — assessment expansion, the cash waterfall, aging math,
fee/fine posting — in fixed-form C-specs and /free blocks.CVUNTLF keys units by association (so *ALL
assessment expansion walks a whole association), and CVLEDLF keys the ledger by due
date (oldest-due-first aging).CVHIST, whose unique key is derived from a stable
business key in a disjoint per-program numeric range. Re-running a program regenerates the same
key, and a CHAIN(EN) guard refuses the duplicate — this is the app's idempotency
mechanism (see F.2).CVGLDIST, created by RUNSQLSTM); CVGLPST is SQLRPGLE that
reads CVHIST and writes balanced rows into it. The AR control report
(CVARRPT) is hand-written ILE COBOL over the keyed ledger.The benefit for operations: each cycle is a small, self-contained program driven from a CL wrapper, and
every money movement lands both on the owner account (CVACCT.ABAL) and in the history trail,
so a run's effect is fully reconstructable.
SETUP/SEED ONLINE BATCH (CL job cycles)
---------- ------ ---------------------
CVSETUP (CRTxxx) CVMENU (5250) CVMONTH --> CVCHRUN (assessment charge)
CVREFLD (seed) opt 1 -> CVUNTIQ CVDAILY --> CVCASHAP (cash application)
opt 2 -> CVLEDIQ CVPERIOD -> CVDELQ (aging+fee+lien)
-> CVVFINE (violation fines)
-> CVRESVTR (reserve transfer)
-> CVGLPST (GL post)
-> CVARRPT (AR control report)
\ /
\ /
v v
CVACCT.ABAL <-- raised by charge/fee/fine, lowered by cash -- the AR invariant
| \
| +--> CVLEDG (owner AR open items: A/S/L/F charges, CHGOPEN reduced by cash)
| +--> CVHIST (durable audit trail, one row per posting, derived key)
+------> CVGLDIST (balanced DR/CR GL distribution) CVRESV/CVRESVH (reserve)
A single posting event (say, the monthly charge run) flows: CVMONTH calls
CVCHRUN → it walks each active assessment schedule, expands *ALL to the
association's occupied units via CVUNTLF → writes a CVLEDG open-item and a
CVHIST row (keyed off account+assessment id) → raises CVACCT.ABAL by the
charge. The date each program processes for is a hard-coded period constant in the source (2026-09 for the
charge run, 2026-09-25 for the period runs) rather than a control table — see the honest note in C.
| Object | Type | Role |
|---|---|---|
| CVASSOC | PF | Association (community) master. |
| CVUNIT | PF | Unit master (composite key ASSOCID+UNITNO). |
| CVUNTLF | LF | Units keyed by ASSOCID (assessment-expansion path). |
| CVOWNER | PF | Owner master. |
| CVASMT | PF | Assessment schedule (regular dues / special levy). |
| CVLEDG | PF | Owner AR open items (the receivables ledger). |
| CVLEDLF | LF | Ledger keyed by CHGDUE (due-date aging path). |
| CVACCT | PF | Unit account: running ABAL + lien status. |
| CVRCPT | PF | Cash receipts. |
| CVVIOL | PF | Code violations. |
| CVARC | PF | Architectural requests. |
| CVRESV | PF | Reserve fund (running RBAL + funding %). |
| CVRESVH | PF | Reserve-transfer history. |
| CVHIST | PF | Durable audit trail (all posting programs). |
| CVGLDIST | SQL table | GL distribution (DR/CR), created by RUNSQLSTM; index CVGLDACC. |
| CVUNTD / CVLEDD / CVMENUD | DSPF | Unit inquiry / ledger-subfile inquiry / menu display files. |
| CVAGEP | PRTF | Delinquency aging printer file. |
| CVREFLD | RPGLE | Reference-data seeder. |
| CVCHRUN | RPGLE | Monthly assessment charge run. |
| CVCASHAP | RPGLE | Daily cash-receipt application. |
| CVDELQ | RPGLE | Period aging + late fee + lien status. |
| CVVFINE | RPGLE | Period violation-fine posting. |
| CVRESVTR | RPGLE | Period reserve-fund transfer. |
| CVGLPST | SQLRPGLE | Period GL post (balanced DR/CR). |
| CVUNTIQ / CVLEDIQ / CVMENU | RPGLE | Unit inquiry / ledger inquiry / menu drivers. |
| CVARRPT | CBLLE | ILE COBOL AR control report. |
| CVSETUP | CLP | Create every object / compile every program. |
| CVDAILY / CVMONTH / CVPERIOD | CLP | The three job-cycle wrappers. |
The full object catalogue is 14 physical files + 2 logical files + 3 display files + 1 printer file + 1 SQL table (+ its index), driven by 10 RPG programs, 1 COBOL program and 4 CL programs, plus 1 seeder. Sections D and F expand each.
CIVICA/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 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 CIVICA — the tested jobs run with
LIBL = QSYS QGPL CIVICA QTEMP and CURLIB = CIVICA (each CL wrapper also issues
its own ADDLIBLE LIB(CIVICA)).
| To do this | Type on the command line |
|---|---|
| Open the main menu (routes to the two inquiries) | CALL CIVICA/CVMENU |
| Unit / owner inquiry directly | CALL CIVICA/CVUNTIQ |
| Owner-ledger inquiry (subfile) directly | CALL CIVICA/CVLEDIQ |
| Run the daily cash-application cycle | CALL CIVICA/CVDAILY (or SBMJOB it) |
| Run the monthly assessment charge cycle | CALL CIVICA/CVMONTH |
| Run the period-end cycle (aging→fines→reserve→GL→AR) | CALL CIVICA/CVPERIOD |
| Rebuild all objects / recompile | CALL CIVICA/CVSETUP then CALL CIVICA/CVREFLD |
The batch drivers and the CL wrappers take no CALL parameters. Only the three inquiry programs
(CVMENU, CVUNTIQ, CVLEDIQ) are interactive; the batch programs run
to completion and each DSPLYs a one-line result banner (e.g. CVCHRUN CHARGED=8 SKIP=...).
Three interactive programs share the display files CVMENUD, CVUNTD and
CVLEDD. All three exit on F3 (DDS CA03).
A two-option menu. Key 1 to call the unit/owner inquiry, 2 for the owner-ledger
inquiry; any other non-blank option shows Invalid option.. Control returns here when the
called inquiry exits.
A non-subfile inquiry. The operator keys an association and a unit and presses
Enter; the program CHAINs the unit (composite key ASSOCID+UNITNO),
then the owner (CVOWNER on OWNERID) and the account
(CVACCT on ACCTNO = ASSOCID || UNITNO), and shows the address, unit status, owner
id/name, account balance (%editc(abal:'K')) and lien status. The status line message walks
Unit not found. → Unit found. → Unit and account found.
as each CHAIN succeeds.
The app's subfile screen. The operator keys a 10-byte account number
(ASSOCID || UNITNO); the program shows the owner name, account balance and lien status, then
loads a subfile (DDS record LSFL under control record LCTL,
SFLPAG(5) per page, SFLSIZ(20), ROLLUP/ROLLDOWN) of the
account's charge/open-item history — one row per CVLEDG charge with its sequence, type,
description, amount, open balance and aging bucket. The subfile is cleared and reloaded on every
enquiry pass (the *IN31 discipline: write LCTL with SFLCLR on, reload, then
display), so a fresh account's items appear immediately.
| Field | Type (DDS) | Shows |
|---|---|---|
| SSEQ | 5S,0 output | Ledger charge sequence (CHGSEQ). |
| STYPE | 1A output | Charge type: A regular, S special, L late fee, F fine. |
| SDESC | 25A output | Charge description (CHGDESC). |
| SAMT | 12A output | Charge as raised (CHGAMT, edited). |
| SOPEN | 12A output | Outstanding open amount (CHGOPEN, edited). |
| SAGE | 1A output | Aging bucket 0/1/2/3 (CHGAGE). |
Honest statement: CIVICA/i does not model a four-eyes maker–checker / separate-authorization workflow. There is no "one user posts, a second user approves" step: charges, cash, fees, fines and reserve transfers are all applied directly by the batch programs. The manual documents the control model the application does have:
A/S), cash applied (P), late fee
(L), fine (F), reserve transfer (V) — carrying the
account/item, amount, date, reference and a memo. This is the after-the-fact accountability trail.CVACCT.ABAL moves only by the charge run (up),
cash application (down) and fee/fine posting (up); the tests assert every account balance equals its
raised-less-applied total to the cent (F.3).'U'
(unapplied); fine posting skips violations not 'O' (open) or still inside their cure
period; reserve transfer skips associations that collected no cash.CVGLPST writes DR/CR pairs so total debits equal total credits
exactly, an independent integrity check on the period's charge book.In sum, the control posture is durable audit + derived-key idempotency + a strict balance invariant + a balanced GL, rather than an approval workflow.
CIVICA/i's processing runs as three CL job cycles rather than one monolithic nightly job: a
daily cycle (cash application), a monthly cycle (assessment charge run), and a
period-end cycle (aging + late fees + lien status, violation fines, reserve transfer, GL post, AR
report). Each CL wrapper is parameterless: it does ADDLIBLE LIB(CIVICA), calls its
program(s), and sends a completion banner.
20260901 (WPERDT/WDUEDT), and the period runs use
run date 20260925 (WRUNDT); CVGLPST's batch is 202609.
There is no LNCTL-style control row to advance between runs. To process a different period you
change these constants in the source and recompile — the cycles are demonstrated against a single
fixed 2026-09 period. This is a documented simplification of the sample, not an operator-facing date knob.| Program (CL cycle) | Purpose | Files touched | Result banner (DSPLY) | Frequency |
|---|---|---|---|---|
| CVCHRUN (CVMONTH) |
Post regular dues to every occupied unit + special assessments; raise ABAL. | CVASMT (U), CVUNIT/CVUNTLF (I), CVACCT (U), CVLEDG (WA), CVHIST (WA). | CVCHRUN CHARGED=n SKIP=m |
Monthly. |
| CVCASHAP (CVDAILY) |
Apply each unapplied receipt to its nominated charge; lower ABAL. | CVRCPT (U), CVLEDG (U), CVACCT (U), CVHIST (WA). | CVCASHAP APPLIED=n SKIP=m |
Daily. |
| CVDELQ (CVPERIOD) |
Age open charges into buckets; assess 10%-of-open late fee; set lien status. | CVLEDG (U), CVACCT (U), CVHIST (WA). | CVDELQ B0=.. B1=.. B2=.. B3=.. FEES=n |
Period. |
| CVVFINE (CVPERIOD) |
Fine open violations past cure period; mark them FINED; raise ABAL. | CVVIOL (U), CVLEDG (WA), CVACCT (U), CVHIST (WA). | CVVFINE FINED=n SKIP=m |
Period. |
| CVRESVTR (CVPERIOD) |
Transfer RPCT% of each association's collected cash into its reserve. | CVRESV (U), CVHIST (WA), CVRESVH (O). | CVRESVTR XFER=n SKIP=m |
Period. |
| CVGLPST (CVPERIOD) |
Post balanced DR/CR GL rows for the period's charges/fees/fines (incremental). | CVHIST (I); SQL CVGLDIST (insert/select). | CVGLPST BATCH=202609 ROWS=n |
Period. |
| CVARRPT (CVPERIOD) |
ILE COBOL AR control report over the ledger (reconciliation totals). | CVLEDG (sequential read). | CVARRPT RAISED/OPEN/ITEMS/OVERDUE |
Period. |
File I/O legend: I=input, U=update, O=output, WA=write/add (output-add). The CL cycles wire
these together: CVDAILY→CVCASHAP; CVMONTH→CVCHRUN;
CVPERIOD→CVDELQ→CVVFINE→CVRESVTR→CVGLPST→CVARRPT.
For every active assessment schedule (ASMTSTAT='A'): a *ALL schedule
walks every occupied unit in the association via CVUNTLF (vacant units are skipped),
posting one CVLEDG open-item (CHGTYPE 'A' regular / 'S' special) per
target account, writing a CVHIST row, and raising CVACCT.ABAL by the charge.
A single-unit special assessment targets just that unit's account. A special assessment's instalment
counter (ASMTPOST) advances once per run and is capped at ASMTINST; a regular
assessment (ASMTINST 0) has no ceiling.
Expected DSPLY against the seeded book (Meadowbrook dues to 3 occupied units + roof special on 0103 + Harborview dues to 4 units = 8 charges over 7 accounts): CVCHRUN CHARGED=8 SKIP=0 8 charges posted; A000010103 gets 2 (regular + special)
Per-account charge totals: A000010101/0102 250.00; A000010103 650.00 (250 dues + 400 roof, two ledger rows); A000020201–0204 320.00 each. Each ABAL rises from 0 by exactly its total.
For every receipt with RCSTAT='U' (unapplied): CHAIN the nominated charge
(composite key ACCTNO+RCCHGSQ), take min(receipt amount, open), reduce CHGOPEN
(fully-applied → CHGSTAT='P'), lower CVACCT.ABAL by the amount applied, write
a CVHIST 'P' row (negative amount), and stamp the receipt
'A' (fully applied) or 'P' (part-applied). An over-payment applies only what is
owed; the remainder stays unapplied.
Expected DSPLY (RC000001 pays A000010101 dues 250.00 in full; RC000002 pays 150.00 of A000010102's 250.00 open): CVCASHAP APPLIED=2 SKIP=0 A000010101 -> 0.00; A000010102 250.00 -> 100.00
Five programs run in order:
((yy×12)+mm)×30+dd): 0 current, 1 = 1–30,
2 = 31–60, 3 = 61+. Pass 2 walks each account, finds its worst open bucket (excluding
'L' late-fee rows), sets LIENSTAT (bucket 1→'D',
2→'N' notice, 3→'L' lien filed, none→blank), and assesses a
flat 10% of the account's open balance as a late fee (once per run date, guarded by the
derived history key). An account with no open charges is cleared to blank and earns no fee.'O' violation whose cure period (VDAYS
from VDT) has elapsed by the run date is fined: post a CVLEDG
'F' charge, raise ABAL, write history, and mark the violation 'F'. A
violation still within cure is left open.'P' history rows for that association's accounts) and transfer RPCT%
into the reserve, raising RBAL and writing a CVRESVH row. Associations that
collected nothing are skipped.1400-OAR (owner receivable) = CR 4100-ASMT +
4300-LATE + 4400-FINE. It posts only the movement since the last close
(the DR receivable already booked is the floor), so a re-run adds nothing.CVLEDG totalling raised
amount, open amount, item count and overdue count.Expected DSPLY across the period chain (seeded book, run date 2026-09-25): CVDELQ B0=0 B1=7 B2=0 B3=0 FEES=6 7 open charges 24d overdue -> bucket 1; 6 accounts fee'd CVVFINE FINED=1 SKIP=1 V1 (24d>10 cure) fined; V2 (5d, in cure) skipped CVRESVTR XFER=1 SKIP=1 Meadowbrook 400.00 cash x 10% = 40.00; Harborview no cash CVGLPST BATCH=202609 ROWS=4 DR receivable + 3 CR revenue lines, balanced CVARRPT ITEMS 00015 8 charges + 6 late fees + 1 fine = 15 ledger rows
Late-fee results: A000010102 → 110.00, A000010103 → 715.00, and each Harborview
account 320.00 → 352.00 (all lien 'D'). A000010101 was fully settled before the close, so
it earns no fee and its lien clears to blank — then later carries a 75.00 violation fine (0 → 75).
All files are in library CIVICA, grounded in the DDS/SQL source in
hoa-app/src/sources.mjs. Dates are stored as signed numerics in YYYYMMDD form
(8S 0); money is packed decimal (11P 2); the account key
ACCTNO is ASSOCID || UNITNO (10A) by construction.
| Field | Type | Meaning |
|---|---|---|
| ASSOCID | 6A | Association id (key), e.g. A00001. |
| ANAME | 30A | Association name. |
| AADDR / ACITY | 30A / 20A | Address / city. |
| AUNITS | 5S 0 | Unit count (informational). |
| ASTAT | 1A | A active. |
| Field | Type | Meaning |
|---|---|---|
| ASSOCID / UNITNO | 6A / 4A | Owning association + unit number (composite key). |
| UADDR | 30A | Unit address. |
| USQFT | 6S 0 | Square footage. |
| UTYPE | 1A | Unit type (e.g. C condo, T townhouse). |
| OWNERID | 6A | Current owner of record (one per unit; blank if vacant). |
| USTAT | 1A | O owner-occupied, R rented, V vacant. |
CVUNTLF is a logical over CVUNIT keyed on ASSOCID
alone (non-unique) so the charge run walks a whole association's units.
| Field | Type | Meaning |
|---|---|---|
| OWNERID | 6A | Owner id (key), e.g. W00001. |
| ONAME | 30A | Owner name. |
| OPHONE | 14A | Phone. |
| OMAIL | 1A | Mail-preference flag (Y/N). |
| OSTAT | 1A | A active. |
| Field | Type | Meaning |
|---|---|---|
| ASSOCID / ASMTID | 6A / 5S 0 | Association + schedule id (key). |
| ASMTUNIT | 4A | *ALL = every occupied unit; otherwise a specific unit number. |
| ASMTTYPE | 1A | R regular (recurring dues), S special (levy). |
| ASMTDESC | 25A | Description (becomes the ledger CHGDESC). |
| ASMTAMT | 11P 2 | Amount charged each posting. |
| ASMTINST | 3S 0 | Instalment ceiling for a special (0 = unlimited, for regular). |
| ASMTPOST | 3S 0 | Instalments posted so far (advanced by the charge run). |
| ASMTSTAT | 1A | A active (only active schedules are charged). |
| Field | Type | Meaning |
|---|---|---|
| ACCTNO / CHGSEQ | 10A / 5S 0 | Unit account + per-account charge sequence (key). |
| CHGTYPE | 1A | A regular assessment, S special, L late fee, F violation fine. |
| CHGDT / CHGDUE | 8S 0 | Charge date / due date (assessments due the 1st of the period). |
| CHGAMT | 11P 2 | Charge as raised (never changed after posting). |
| CHGOPEN | 11P 2 | Outstanding amount (CHGAMT less cash applied). |
| CHGAGE | 1S 0 | Aging bucket stamped by CVDELQ: 0/1/2/3. |
| CHGDESC | 25A | Free-text description. |
| CHGSTAT | 1A | O open, P part/fully-applied (open reduced by cash). |
CVLEDLF is a logical over CVLEDG keyed on
CHGDUE, ACCTNO so the aging run reads oldest-due-first without sorting.
| Field | Type | Meaning |
|---|---|---|
| ACCTNO | 10A | Owner-account key = ASSOCID || UNITNO (key). |
| ASSOCID / UNITNO | 6A / 4A | The association and unit this account belongs to. |
| ABAL | 11P 2 | Running owner-ledger balance (the AR invariant). |
| LIENSTAT | 1A | ' ' none, D delinquent, N notice sent, L lien filed. |
| Field | Type | Meaning |
|---|---|---|
| RCPTNO | 8A | Receipt number (key), e.g. RC000001. |
| ACCTNO | 10A | Account the receipt pays. |
| RCCHGSQ | 5S 0 | The ledger charge sequence this receipt nominates. |
| RCDT | 8S 0 | Receipt date. |
| RCAMT / RCAPPL | 11P 2 | Receipt amount / amount actually applied. |
| RCSTAT | 1A | U unapplied, A fully applied, P part-applied. |
| Field | Type | Meaning |
|---|---|---|
| VIOLNO | 6S 0 | Violation number (key). |
| ACCTNO | 10A | Account fined. |
| VDT | 8S 0 | Reported date. |
| VCODE / VDESC | 4A / 30A | Violation code / description. |
| VDAYS | 3S 0 | Cure period in days before a fine is assessed. |
| VFINEAMT | 11P 2 | Fine amount. |
| VSTAT | 1A | O open, F fined, C cured/closed. |
| Field | Type | Meaning |
|---|---|---|
| ARCNO | 6S 0 | Request number (key). |
| ACCTNO | 10A | Requesting owner's account. |
| ARDT / ARDESC | 8S 0 / 30A | Request date / description. |
| ARSTAT | 1A | P pending, A approved, D denied. |
Architectural requests are inventory only in this build — the file exists and is seeded/queryable, but no batch program posts money against it.
| Field | Type | Meaning |
|---|---|---|
| CVRESV.ASSOCID | 6A | Association (key, one reserve row each). |
| CVRESV.RBAL | 11P 2 | Running reserve balance. |
| CVRESV.RPCT | 5P 2 | Funding percent of collected cash transferred each period. |
| CVRESVH.ASSOCID / RSEQ | 6A / 5S 0 | Reserve-transfer history key. |
| CVRESVH.RDT / RAMT | 8S 0 / 11P 2 | Transfer date / amount. |
| Field | Type | Meaning |
|---|---|---|
| HSEQ | 8S 0 | Derived history key (unique; disjoint ranges per program — see F.2). |
| ACCTNO / HITEM | 10A / 10A | Account / item reference (account, violation no, or association). |
| HTYPE | 1A | A/S charge, P payment, L late fee, F fine, V reserve transfer. |
| HDT / HAMT | 8S 0 / 11P 2 | Date / amount (payments carry a negative amount). |
| HREF / HMEMO | 8A / 25A | Reference / memo. |
| Field | Type | Meaning |
|---|---|---|
| GLSEQ | DECIMAL(8,0) | Distribution sequence (PK). |
| GLBATCH | DECIMAL(6,0) | Batch (the period, e.g. 202609). |
| ACCT | CHAR(9) | GL account: 1400-OAR, 4100-ASMT, 4300-LATE, 4400-FINE. |
| DRCR | CHAR(1) | D debit / C credit. |
| AMT | DECIMAL(11,2) | Amount. |
| GLREF / GLDT | CHAR(8) / DECIMAL(8,0) | Reference / date. |
Index CVGLDACC on (ACCT, DRCR) supports the incremental-post
floor query and the reconciliation SELECTs.
2 associations (Meadowbrook A00001, Harborview A00002), 9 units (2 vacant, 2 rented, 5 owner-occupied), 6 owners, 3 assessment schedules (Meadowbrook dues 250.00 + roof special 400.00 on 0103, Harborview dues 320.00), 7 unit accounts (one per occupied unit, zero balance), 2 reserve funds (Meadowbrook 10%, Harborview 8%).
CALL CIVICA/CVSETUP creates every object and compiles
every program, then CALL CIVICA/CVREFLD seeds the reference book. Confirm the
setup-complete banner and the seed counts (ASSOC=2 UNIT=9 OWNER=6 ASMT=3 ACCT=7).SBMJOB CMD(CALL PGM(CIVICA/CVMONTH)); check
CVCHRUN CHARGED=n SKIP=m and that each occupied account's ABAL rose by its dues (plus
any special).CVRCPT rows (RCSTAT 'U',
each naming an account + charge sequence), then SBMJOB CMD(CALL PGM(CIVICA/CVDAILY));
check CVCASHAP APPLIED=n and that balances fell by exactly what was applied.CALL CIVICA/CVMENU → option 1 (unit/owner) or 2 (owner ledger
subfile) for read-only inquiry and reconciliation.Pre-checks: confirm the job's library list includes CIVICA; confirm the seed data
is loaded (the inquiries show data). Post-checks: every batch program's DSPLY banner shows the
expected count; the SKIP figure accounts for anything not processed (inactive schedule,
already-applied receipt, vacant unit).
CVVIOL (RCSTAT 'O', with a
cure period VDAYS).SBMJOB CMD(CALL PGM(CIVICA/CVPERIOD)). It runs
aging→fines→reserve→GL→AR and sends the period-end banner.CVDELQ B0/B1/B2/B3 bucket counts and FEES=n;
each fee'd account is now LIENSTAT='D' (or N/L if deeper) with ABAL raised 10% of open.CVVFINE FINED=n; each fined violation is VSTAT='F' and
its account carries the fine; violations still within cure stay 'O'.CVRESVTR XFER=n; check CVRESVH and each
CVRESV.RBAL rose by exactly RPCT% of that association's collected cash.SELECT DRCR, SUM(AMT) FROM CIVICA.CVGLDIST GROUP BY DRCR;
-- total D must equal total C exactly; DR 1400-OAR = 4100-ASMT + 4300-LATE + 4400-FINE
Reconciling figures (the same ones the cycle test checks against a hand-derived oracle):
DR Owner Receivable = assessments (2210.00) + late fees (203.00) + fine (75.00) = 2488.00; CR Assessment
Revenue 2210.00, CR Late Fee 203.00, CR Fine 75.00. CVARRPT reports the full ledger:
8 charges + 6 late fees + 1 fine = 15 items.
Each batch program DSPLYs a result banner with a processed count and a SKIP count. The app's
safety net is derived-key idempotency: re-running a program regenerates the same
CVHIST key and the CHAIN(EN) guard refuses the duplicate.
| Situation | Behaviour | Action |
|---|---|---|
| Charge run fails partway | Already-charged accounts have their CVHIST row; the rest do not. | Re-submit CVMONTH: charged accounts are skipped (SKIP rises), the rest catch up. Idempotent per period. |
| Re-run the same charge run | All keys already exist → every account skipped. | Safe no-op; the special assessment's ASMTPOST does not advance twice. |
| Cash application re-run | Each receipt already has its 20000000+ history row. | APPLIED=0 SKIP=n; balances do not fall twice. |
| Aging/late-fee re-run (same run date) | Each account already has its 30000000+ late-fee row. | No double fee; buckets/lien status are re-stamped harmlessly. |
| Fine re-run | Fined violation is 'F' and already has its 40000000+ row. | Never double-fined; still-in-cure violations remain open for a later run. |
| Reserve transfer re-run | Each association already has its 50000000+ row. | No second CVRESVH row; RBAL unchanged. |
| GL post re-run | Incremental post: DR already booked is the floor. | Adds no rows when nothing new has been charged (ROWS=0 effectively). |
| Whole CVPERIOD re-run | Every step idempotent. | The CL chain is safely re-runnable end to end. |
CVHIST (and mirrored on
CVACCT.ABAL and the CVLEDG open items), any cycle's effect is fully
reconstructable after the fact for reconciliation and recovery. CVARRPT gives an independent
COBOL total over the ledger to reconcile against the account balances.The complete program surface, from hoa-app/src/sources.mjs. All objects are in library
CIVICA. The RPG is a mix of fixed-form C-specs and /free blocks; each program
uses keyed record-level I/O against the DDS files.
CVREFLD ASSOC=2 UNIT=9 OWNER=6 ASMT=3 ACCT=7.CVASMT; subroutine ASM1 expands one schedule (*ALL via
CVUNTLF, else one unit), POST1 posts a CVLEDG open-item +
CVHIST row and raises CVACCT.ABAL. Re-CHAINs the schedule row by its own
AKEY before advancing ASMTPOST for a special (the unit walk moved the shared
record buffer). DSPLYs CVCHRUN CHARGED=n SKIP=m.CVRCPT; subroutine CSH1 takes min(RCAMT, CHGOPEN) against
the nominated charge (CKEY), reduces the open item, lowers ABAL, writes a negative-amount
'P' history row and stamps the receipt A/P. DSPLYs CVCASHAP APPLIED=n SKIP=m.CHGAGE from a 30/360 day-serial; pass 2 (LF1) finds each
account's worst open bucket, sets LIENSTAT, and assesses a 10%-of-open late fee (one per
run date). DSPLYs CVDELQ B0=.. B1=.. B2=.. B3=.. FEES=n.CVVIOL; subroutine VF1 fines an open violation whose cure period has
elapsed (30/360 day-serial), posts an 'F' ledger + history row, raises ABAL, and
re-CHAINs the violation to mark it 'F'. DSPLYs CVVFINE FINED=n SKIP=m.CVRESV; subroutine RSV1 sums the association's 'P' cash
history, transfers RPCT%, writes a CVRESVH + 'V' history row and
raises RBAL. DSPLYs CVRESVTR XFER=n SKIP=m.CVHIST by type, computes gross less the already-posted DR floor
(SELECT SUM(AMT) ... WHERE ACCT='1400-OAR' AND DRCR='D'), and inserts balanced DR/CR rows
into CVGLDIST via glrow. DSPLYs CVGLPST BATCH=202609 ROWS=n.CVUNTD (plain unit/owner), CVLEDD (ledger
subfile with SFLCLR/reload discipline) and CVMENUD (routes to the two inquiries).CVLEDG, totalling RAISED, OPEN, ITEMS and OVERDUE; DISPLAYs
four CVARRPT ... lines.SNDPGMMSG banner.The idempotency mechanism is a stable derived history key, one disjoint numeric range per
program, so a re-run regenerates the same CVHIST.HSEQ and the CHAIN(EN) guard
refuses the duplicate. No control row, no run-flag — the derived key is the guard.
| Program | HTYPE | HSEQ derivation |
|---|---|---|
| CVCHRUN | A / S | 10000000 + (account-digits × 100) + ASMTID |
| CVCASHAP | P | 20000000 + receipt-digits |
| CVDELQ | L | 30000000 + account-digits |
| CVVFINE | F | 40000000 + violation number |
| CVRESVTR | V | 50000000 + association-digits |
"account-digits" is %dec(%subst(ACCTNO:2:9):9:0) (the numeric tail of
ASSOCID||UNITNO); "receipt-digits" is the numeric tail of RCPTNO; "association-digits"
the numeric tail of ASSOCID. The ranges are 10M apart so no two programs collide.
The core correctness property the cycle test asserts to the cent: CVACCT.ABAL moves
only by (up) the charge run, (down) cash application, and (up) fee/fine posting. Concretely, for the
seeded book after the full cycle:
Each RPG program re-CHAINs the account (and the schedule/violation) after any intervening file walk, because the walk moved the shared record buffer; the CHAIN reloads the whole record from disk before the in-memory field is updated. This is the discipline that keeps ABAL exact under record-level I/O.
*LOVAL SETLL then
READ to EOF on an indicator), or walk a keyed group (WASSOC SETLL CVUNTLF +
READE).AKEY, CKEY,
UKEY); CHAIN(EN) against CVHIST is the idempotency probe (fetch
without lock, act on the not-found indicator).EXEC SQL SELECT ... INTO for the posted-floor
and max-sequence, EXEC SQL INSERT per GL row, checking sqlcod before counting
a posted row.CIVICA/i has no SQLSTATE application-error surface (it is not SQL-PL). Behaviour is gated by field status codes:
| Field | Values | Effect |
|---|---|---|
| CVASMT.ASMTSTAT | A | Only active schedules are charged; others SKIPped. |
| CVUNIT.USTAT | O/R/V | *ALL expansion skips vacant (V) units. |
| CVRCPT.RCSTAT | U/A/P | Only U (unapplied) receipts are applied; result A (full) or P (part). |
| CVLEDG.CHGSTAT | O/P | A fully-applied charge flips O→P (open reduced to 0). |
| CVVIOL.VSTAT | O/F/C | Only O past cure is fined → F; C is skipped; still-in-cure stays O. |
| CVACCT.LIENSTAT | ' '/D/N/L | Set from worst open bucket: 1→D, 2→N, 3→L, none→blank. |
| CVLEDG.CHGAGE | 0/1/2/3 | 30/360 aging bucket: current / 1-30 / 31-60 / 61+. |
| CVGLDIST.DRCR | D/C | Debit / credit; total D must equal total C. |
D delinquent, N
notice sent, L lien filed; blank when current.SBMJOB CMD(CALL PGM(CIVICA/CVPERIOD))).