ESTATE/i is a fixed-asset accounting application: acquisition and capitalisation,
three depreciation methods (straight-line, declining-balance, units-of-production) posted monthly with
a GL distribution, asset transfer between cost centres, disposal with gain/loss, weekly maintenance and
warranty sweeps, and a year-end register with a fully-depreciated retirement roll. The business logic is
classic RPG IV over keyed DDS physical/logical files, with one ILE COBOL summary program and one
SQLRPGLE program that posts the GL distribution to a DB2 for i table; the four periodic cycles are driven
by CL. This manual is the reference for the operator who runs the periodic cycles and the online inquiry
screens, and for the developer maintaining the application. It is grounded entirely in the committed
source (assets-app/src/sources.mjs, src/seed.mjs, and the
test/fa_*.mjs drivers), which loads library ESTATE.
ESTATE/i accounts for the full life of a fixed asset:
ASTAT='P'
(pending), stamp its in-service date from the acquisition date, set NBV = cost, move it to
'A' (in service), and post a capitalisation row to the asset ledger.S, declining-balance D,
units-of-production U), write a depreciation-history row, roll the master's accumulated
depreciation and net book value, then distribute the charge to the general ledger.'D'
(disposed), and post a signed gain/loss ledger row.'F', re-roll every asset's
NBV under the invariant, generate the year's asset register and print it.The application is classic IBM i: RPG IV programs orchestrate keyed DDS files, one ILE COBOL
program produces an independent depreciation summary, one SQLRPGLE program posts the GL distribution
with embedded SQL, and CL ties the objects together into a setup job and four periodic cycles.
Everything lives in library ESTATE.
(ASTNO, DPER) key
both accumulates history across months and is the guard that makes a same-period re-run a no-op —
the single most important control in the app (see F.2).COST - SALVAGE and NBV can
never go negative (see F.1).There is no CICS, no transaction-id switch and no single monolithic nightly job: work is organised as a daily, a weekly, a monthly and an annual CL cycle, each calling a small set of RPG/COBOL programs in a fixed order.
ONLINE (5250) DAILY FADAILY MONTHLY FAMONTH
------------ -------------- ---------------
FAMENU FAACQ (capitalise) FADEPRUN (depreciate, 3 methods)
opt 1 -> FAASTIQ FAXFRUN (transfer) FAGLPST (GL distribution, SQL)
opt 2 -> FADEPIQ FADISPOS (dispose) FADEPSUM (COBOL proof summary)
WEEKLY FAWEEK ANNUAL FAYEAR
------------- --------------
FAMTSWP (maint due) FARETIRE (retire fully-depreciated)
FAWARN (warranty) FANBVRL (NBV roll, invariant)
FACCRPT (cctr summary) FAREGEN (register snapshot) -> FAREGPR (print)
+-- reads policy -- FACLASS
|
FAASSET <--------- every program -- (master; keyed also by cctr via FAASTLF)
| \
| +--> FADEPH (per-asset-per-period history; -> FAGLDIST via FAGLPST)
| +--> FALEDG (C capitalise / X transfer / S sale / P depn / F retire)
+------> FAREG (year-end register snapshot) FADEPPER (data area: period)
A depreciation event flows: operator sets the period in data area FADEPPER → submits
FAMONTH → FADEPRUN reads the period, walks every in-service asset, computes
the charge by method, writes a FADEPH row and rolls the master → FAGLPST
reads the period through the period-keyed logical and writes balanced DR/CR rows to FAGLDIST
→ FADEPSUM (COBOL) independently totals the period by method as a proof.
| Object | Type | Role |
|---|---|---|
| FAASSET | PF | Asset master (the spine), keyed by ASTNO. |
| FAASTLF | LF | Asset master keyed by cost centre (CCTR, ASTNO). |
| FACLASS | PF | Asset-class master: default depreciation policy + GL accounts. |
| FACCTR | PF | Cost-centre master. |
| FADEPH | PF | Depreciation history, composite key (ASTNO, DPER). |
| FADEPLF | LF | Depreciation history keyed by period (DPER, ASTNO). |
| FAXFER | PF | Transfer request log, keyed by XSEQ. |
| FADISP | PF | Disposal request log, keyed by ASTNO. |
| FAMAINT | PF | Maintenance schedule, composite key (ASTNO, MSEQ). |
| FALEDG | PF | Durable accumulating asset ledger, keyed by LSEQ. |
| FAREG | PF | Year-end register snapshot, composite key (RYEAR, ASTNO). |
| FAGLDIST | SQL table | GL distribution rows (DR/CR), keyed by GLSEQ. |
| FAASTD / FADEPD / FAMENUD | DSPF | Inquiry / subfile / menu display files. |
| FAREGP | PRTF | Fixed-asset register printer file. |
| FADEPPER | *DTAARA | CHAR(10) data area carrying the depreciation period. |
| FAREFLD | RPGLE | Reference-data / seed loader (classes, cost centres, assets, maintenance). |
| FAACQ / FAXFRUN / FADISPOS | RPGLE | Daily: capitalise / transfer / dispose. |
| FADEPRUN | RPGLE | Monthly depreciation calculation & posting. |
| FAGLPST | SQLRPGLE | Monthly GL distribution (embedded SQL). |
| FADEPSUM | CBLLE | ILE COBOL depreciation summary (independent proof). |
| FAMTSWP / FAWARN / FACCRPT | RPGLE | Weekly: maintenance sweep / warranty report / cost-centre summary. |
| FARETIRE / FANBVRL / FAREGEN / FAREGPR | RPGLE | Annual: retire / NBV roll / register generate / register print. |
| FAASTIQ / FADEPIQ / FAMENU | RPGLE | Online: asset inquiry / depreciation-history subfile / menu. |
| FASETUP | CLP | Creates every object & compiles every program. |
| FADAILY / FAWEEK / FAMONTH / FAYEAR | CLP | The four periodic job cycles. |
The catalogue is 11 DDS files (9 PF + 2 LF) + 1 SQL table + 1 data area, 3 display files and 1 printer file, driven by 16 RPG programs, 1 COBOL program and 5 CL programs. Sections D and F expand each.
ESTATE/i has no CICS transaction identifiers and no transid switch. On IBM i, each program is
reached by name from a 5250 command-entry line (or a JOBQ/scheduler for the batch cycles). The
operator equivalent of "type a transid and Enter" is "type a CALL command and Enter". Before
invoking anything, the job's library list must include ESTATE — the tested jobs run
with LIBL = QSYS QGPL ESTATE QTEMP and CURLIB = ESTATE.
| To do this | Type on the command line |
|---|---|
| Open the operator menu (both inquiries) | CALL ESTATE/FAMENU |
| Open the asset inquiry screen directly | CALL ESTATE/FAASTIQ |
| Open the depreciation-history subfile directly | CALL ESTATE/FADEPIQ |
| Build/compile the whole application | CALL ESTATE/FASETUP |
| Load reference/seed data | CALL ESTATE/FAREFLD |
| Run the daily / weekly cycle | CALL ESTATE/FADAILY / FAWEEK (or SBMJOB) |
| Set the depreciation period, then run monthly | CHGDTAARA DTAARA(ESTATE/FADEPPER) VALUE(' 202608') then CALL ESTATE/FAMONTH |
| Run the annual register roll | CALL ESTATE/FAYEAR |
The batch programs take no CALL parameters. The daily, weekly and annual programs read the run
date they need from constants compiled into the source (see the note in C.2); the monthly depreciation and
GL programs read the processing period from the FADEPPER data area (default
' 202608'), so a scheduled submission sets that data area, then simply calls the cycle.
Only FAMENU, FAASTIQ and FADEPIQ are interactive.
Honest statement: the online surface is read-only inquiry plus a menu. There is no maintenance screen: an operator cannot capitalise, transfer, dispose, depreciate or edit an asset from 5250. Those are all batch actions (section C). The three interactive programs are:
FAMENU presents a two-option menu and routes to the inquiries by a program-to-program
CALL: option 1 calls FAASTIQ, option 2 calls FADEPIQ,
any other non-blank option shows Invalid option. on the menu itself. F3
ends the program.
A non-subfile screen: key an asset number in IAST and press Enter;
the program CHAINs FAASSET and renders the master record with the net book
value derived on screen as COST - ACCDEP (work field WN, edited with
%editc(...:'K')). An unknown key shows Asset not found. and blanks every value
rather than leaving the previous asset's figures stale.
| Field | DDS | Shows |
|---|---|---|
| IAST | 8A input/output | Asset number keyed by the operator. |
| DDESC / DCLS / DCC | output | Description, class code, cost centre. |
| DMTH | 1A output | Depreciation method (S / D / U). |
| DCOST / DSALV / DACC | 14A output | Cost, salvage, accumulated depreciation (edited). |
| DNBVF | 14A output | Derived net book value = cost − accumulated. |
| DSTS | 1A output | Status (P/A/D/F). |
| DMSG | 50A output | Result message (found / not found). |
A subfile screen (DDS record DSFL under control record DCTL,
SFLPAG(0005) per page, SFLSIZ(0020)): key an asset, and the program loads that
asset's period charges from FADEPH (via a keyed READE group) into the subfile,
showing period, method, charge, running accumulated and running NBV. It uses a classic load-then-display
discipline: it writes SFLCLR (indicator 31) to wipe the previous enquiry, resets the relative
record number, then re-loads the rows for the asset actually asked for before the next EXFMT.
Roll Up/Roll Down page a longer history (an
asset with 6 periods shows 5 on page one, the 6th on Roll Up).
SPER SMTH SAMT SACC SNBV) are all output; the operator
keys only the asset number (IDAST). Re-enquiring on an asset with no history correctly clears
the subfile to zero rows and still resolves the header. These are the shipped screens exercised by
test/fa_interactive.mjs over the real in-process 5250 session.Honest statement: ESTATE/i does not model a four-eyes maker–checker / separate-authorization workflow, and it has no online data-entry at all. A capitalisation, transfer, disposal or depreciation posting is actioned by its batch program the next time the cycle runs; there is no "one user requests, a second user approves" gate in the code. The control model the application does have is:
FAXFER.XSTAT='R' (requested) rows transfer and only FADISP.DSTAT='R' rows
dispose; an actioned request flips to 'C' (complete) so it is never re-actioned. A
transfer to a non-existent or closed cost centre is refused and the request stays open.ASTAT='P' assets capitalise; only 'A' assets
depreciate, are swept for maintenance, or retire; a disposed 'D' asset is skipped by
the register and re-disposal.fa_* suites assert repeatedly.COST - SALVAGE and NBV can never go negative, enforced in the calculation and re-asserted
by the annual NBV roll (see F.1).In sum the control posture is request/state gating + a durable ledger + idempotency + the depreciable-base invariant, all in the batch/data layer, rather than an online segregation-of-duties workflow.
ESTATE/i runs as four CL cycles rather than one monolithic nightly job: a daily cycle
(acquisition, transfer, disposal), a weekly cycle (maintenance sweep, warranty report, cost-centre
summary), a monthly cycle (depreciation, GL distribution, COBOL summary) and an annual cycle
(retirement, NBV roll, register generate + print). Each cycle is a CL program that ADDLIBLEs
ESTATE, CALLs its programs in a fixed order and sends a completion banner.
-- the monthly cycle takes its period from a data area; set it, then submit CHGDTAARA DTAARA(ESTATE/FADEPPER) VALUE(' 202608') SBMJOB CMD(CALL PGM(ESTATE/FAMONTH)) JOB(FAMONTH) -- the other cycles are parameterless SBMJOB CMD(CALL PGM(ESTATE/FADAILY)) JOB(FADAILY)
| Program | Cycle | Purpose | Reads | Writes / DSPLY |
|---|---|---|---|---|
| FAACQ | Daily | Capitalise every pending ('P') asset. | FAASSET, FACLASS | Asset→'A', INSVDT, NBV; FALEDG 'C'; FAACQ CAPITALISED=n SKIP=n. |
| FAXFRUN | Daily | Action 'R' transfer requests to a valid open cost centre. | FAXFER, FAASSET, FACCTR | Asset CCTR moved; request→'C'; FALEDG 'X'; FAXFRUN TRANSFERRED=n SKIP=n. |
| FADISPOS | Daily | Action 'R' disposal requests; compute NBV & gain/loss. | FADISP, FAASSET | Asset→'D', NBV 0; request→'C'; FALEDG 'S' (signed); FADISPOS DISPOSED=n SKIP=n. |
| FAMTSWP | Weekly | Flag maintenance schedules due on/before the sweep date. | FAMAINT, FAASSET | Schedule→'D'; FAMTSWP DUE=n SKIP=n COST=n. |
| FAWARN | Weekly | Report schedules whose warranty has expired. | FAMAINT, FAASSET | Per-asset DSPLY + FAWARN EXPIRED=n INWARR=n. |
| FACCRPT | Weekly | Cost-centre asset summary over the CCTR access path. | FACCTR, FAASTLF | Per-centre DSPLY FACCRPT cc N=n COST=n NBV=n. |
| FADEPRUN | Monthly | Depreciate every in-service asset for the period (3 methods). | FAASSET, FADEPH, FADEPPER | FADEPH row + master roll; FALEDG basis; FADEPRUN PER=p POSTED=n SKIP=n. |
| FAGLPST | Monthly | GL distribution: balanced DR/CR by cost centre (embedded SQL). | FADEPLF, FAASSET, FACCTR, FADEPPER | FAGLDIST rows; FAGLPST PER=p ROWS=n TOTAL=n. |
| FADEPSUM | Monthly | ILE COBOL: total the period's history by method (proof). | FADEPH | DSPLY STRAIGHT / DECLINE / UNITS / TOTAL / ROWS. |
| FARETIRE | Annual | Retire fully-depreciated in-service assets. | FAASSET, FALEDG | Asset→'F'; FALEDG 'F'; FARETIRE RETIRED=n SKIP=n. |
| FANBVRL | Annual | Re-roll NBV = cost−accum, clamping to the invariant. | FAASSET | Master NBV; FANBVRL ASSETS=n NBV=n CLAMPED=n. |
| FAREGEN | Annual | Snapshot one register row per live asset for the year. | FAASSET, FAREG | FAREG rows; FAREGEN YEAR=y LINES=n NBV=n SKIP=n. |
| FAREGPR | Annual | Print the register (PRTF, page overflow). | FAREG, FAASSET, FAREGP | Spooled register; FAREGPR LINES=n NBV=n. |
| FAREFLD | Setup | Load reference & seed data. | — | FACLASS, FACCTR, FAASSET (pending), FAMAINT; FAREFLD CLASSES=4 CCTR=3 ASSETS=7 MAINT=4. |
FAACQ reads the master sequentially; each asset at ASTAT='P' is capitalised: derive
the stable ledger key 10000000 + %dec(%subst(astno:3:6):8:0), refuse (skip) if that ledger
row already exists, otherwise CHAIN the class for policy, write the FALEDG 'C' row, re-CHAIN
the asset, stamp INSVDT=ACQDT, set NBV=COST-ACCDEP, move to 'A'.
FAXFRUN walks FAXFER; for a 'R' request whose destination cost centre
exists and is open (CCSTAT='A'), it moves the asset's CCTR, writes a FALEDG 'X'
row keyed 20000000 + XSEQ, and flips the request to 'C' (a transfer carries NBV
as information only — cost/accum/NBV do not change). FADISPOS walks FADISP; for a
'R' request it snaps cost and accumulated, computes NBV = cost−accum and
gain/loss = proceeds−NBV, zeroes the asset's NBV, moves it to 'D', writes a signed
FALEDG 'S' row (memo GAIN or LOSS) keyed 30000000 + asset digits, and closes the request.
Expected DSPLY (7 seeded pending assets, 1 valid + 1 bad transfer, 2 disposals): FAACQ CAPITALISED= 7 SKIP= 0 capitalised value totals 247200.00 FAXFRUN TRANSFERRED= 1 SKIP= 1 CC99 destination refused FADISPOS DISPOSED= 2 SKIP= 0 gain +2000.00, loss -3500.00
FAMTSWP flags every MSTAT='S' schedule whose NEXTDT falls on or before
the sweep date (compiled constant WRUNDT = 20260815) whose asset is still in service, setting
it 'D' and accumulating its MCOST. FAWARN counts and names schedules whose
WARRDT has passed the run date. FACCRPT reads each cost centre's assets through the
CCTR-keyed logical FAASTLF (a genuine alternate access path — a keyed READE
group, not a full-master scan per centre) and summarises count / cost / NBV.
Expected DSPLY (sweep 20260815, all 7 assets in service): FAMTSWP DUE= 2 SKIP= 2 COST= 1070.00 750.00 + 320.00 FAWARN EXPIRED= 1 INWARR= 3 AS100002 out of warranty FACCRPT CC10 N= 4 COST= 123200.00 ... CC20 N=1, CC30 N=2 FACCRPT CENTRES DONE ASSETS= 7
FADEPRUN reads the period from data area FADEPPER (falling back to
202608 if the data area is blank/invalid), then for every ASTAT='A' asset with
depreciable base remaining computes the charge by method (F.1), clamps it to the remaining base,
and — guarded by the composite (ASTNO, DPER) key so a same-period re-run posts nothing
— writes a FADEPH row and rolls the master's ACCDEP, NBV and
LSTPER. FAGLPST reads the whole period through the period-keyed logical
FADEPLF, and for each charge writes a balanced pair to FAGLDIST:
DR 7100-DEPN (expense) and CR 1590-ADEP (accumulated depreciation), tagged with
the asset's cost centre; it is idempotent — if the period already has distribution rows it reports
ALREADY POSTED and adds nothing. FADEPSUM (COBOL) independently totals the period's
history by method as a cross-check.
Expected DSPLY (period 202608, 7 in-service assets): FADEPRUN PER=202608 POSTED= 7 SKIP= 0 period total 8450.00 FAGLPST PER=202608 ROWS= 14 TOTAL= 8450.00 2 rows/asset, DR=CR=8450.00 FADEPSUM STRAIGHT 2250.00 900 + 750 + 600 FADEPSUM DECLINE 1600.00 1000 + 600 FADEPSUM UNITS 4600.00 3600 + 1000 FADEPSUM TOTAL 8450.00
POSTED=0 SKIP=7 and leaves
accumulated depreciation unchanged (composite (ASTNO, DPER) guard) — the single most
important control in the app. The whole FAMONTH chain is re-runnable.FARETIRE retires any in-service asset whose ACCDEP has reached its depreciable base
(COST-SALVAG) to 'F' and posts a FALEDG 'F' row keyed
60000000 + asset digits. FANBVRL re-computes NBV = COST - ACCDEP for every
asset, clamping accumulated to the base and NBV to non-negative (disposed assets carry NBV 0), reporting
how many it had to clamp. FAREGEN writes one FAREG snapshot row per non-disposed asset
for the register year (compiled constant WYR = 2026), composite-keyed (RYEAR, ASTNO)
so a re-run is refused. FAREGPR prints the register through FAREGP with page overflow.
Expected DSPLY (after the three depreciation periods in the cycles suite): FARETIRE RETIRED= 1 SKIP= 6 the fully-depreciated jig AS400001 FANBVRL ASSETS= 7 NBV=... CLAMPED= 0 FAREGEN YEAR=2026 LINES= 7 NBV=... SKIP= 0 FAREGPR LINES= 7 NBV=...
FASETUP creates every object and compiles every program;
FAREFLD loads reference data and the seven pending assets. FAACQ (daily)
capitalises them before anything can depreciate.FADEPRUN posts the history and rolls
the master, then FAGLPST distributes that history to the GL, then FADEPSUM
proves the totals. GL distribution must follow depreciation or it finds nothing for the period.USEUNIT on any units-of-production asset before the run — the depreciation
program consumes the units used in the period).All files are in library ESTATE, grounded in src/sources.mjs. Dates are stored
as signed numeric in YYYYMMDD form (periods as YYYYMM); money is packed
11P2; rates are packed 7P4 (0.2000 = 20% a year, or a per-unit amount for
units-of-production). NBV is always COST - ACCDEP — derived, but also carried on the
master so the register prints without recomputation.
| Field | Type | Meaning |
|---|---|---|
| ASTNO | 8A | Asset number (key), e.g. AS100001. |
| ADESC | 30A | Description. |
| ACLASS | 4A | Asset class (→ FACLASS). |
| CCTR | 4A | Cost centre (→ FACCTR); transfer target. |
| ACQDT / INSVDT | 8S 0 | Acquisition date / in-service date (stamped at capitalisation). |
| COST | 11P 2 | Original cost. |
| SALVAG | 11P 2 | Salvage / residual value. |
| LIFEMT | 4S 0 | Life in MONTHS (straight-line divisor). |
| DMETH | 1A | Method: S straight-line, D declining-balance, U units-of-production. |
| DRATE | 7P 4 | Annual declining-balance rate, or per-unit amount for units-of-production. |
| TOTUNIT / USEUNIT | 9P 0 | Lifetime units / units used this period (set before the monthly run). |
| ACCDEP | 11P 2 | Accumulated depreciation. |
| NBV | 11P 2 | Net book value (cost − accumulated). |
| ASTAT | 1A | P pending, A in service, D disposed, F fully depreciated / retired. |
| LSTPER | 6S 0 | Last period depreciated (YYYYMM). |
Seeded (all pending 'P'): AS100001 PLANT 60000/6000/60m/S; AS100002 VEHICLE 40000/4000/48m/S; AS200001 IT 60000/rate .2000/D; AS200002 SERVER 24000/rate .3000/D; AS300001 PRESS 50000/5000/rate .4500/unit/U; AS300002 MOULD 12000/rate .2000/unit/U; AS400001 FIXTURE 1200/0/2m/S (retires fast).
Non-unique alternate access path so the cost-centre register (FACCRPT) and transfer audit are keyed
READE reads rather than a full-master scan. A cost centre holds many assets.
| Field | Type | Meaning |
|---|---|---|
| CLSCD | 4A | Class code (key), e.g. PLNT / VEHC / ITEQ / TOOL. |
| CLSDSC | 25A | Class description. |
| DFMETH / DFLIFE / DFRATE | 1A / 4S 0 / 7P 4 | Default method / life / rate (the policy where the asset is silent). |
| ASSACC / DEPACC / EXPACC | 9A | Asset, accumulated-depreciation and depreciation-expense GL accounts. |
Seeded: PLNT (S/60/7100-DEPN), VEHC (S/48/7110-DEPN), ITEQ (D/36/.2000/7120-DEPN), TOOL (U/0/.4500/7130-DEPN).
| Field | Type | Meaning |
|---|---|---|
| CCNO | 4A | Cost-centre number (key). |
| CCDESC / CCMGR | 25A / 20A | Description / manager. |
| CCSTAT | 1A | Status; a transfer target must be 'A' (open). |
Seeded: CC10 MAIN FACTORY, CC20 DISTRIBUTION DEPOT, CC30 HEAD OFFICE (all open).
| Field | Type | Meaning |
|---|---|---|
| ASTNO / DPER | 8A / 6S 0 | Asset + period YYYYMM (key). The composite key accumulates history and guards re-runs. |
| DMETH | 1A | Method used for this charge. |
| DAMT | 11P 2 | Period charge. |
| DACCUM | 11P 2 | Running accumulated depreciation AFTER this charge. |
| DNBV | 11P 2 | Net book value after this charge. |
| DPDT | 8S 0 | Posting date. |
Alternate access path so the monthly GL distribution (FAGLPST) reads a whole period with one keyed
READE group rather than scanning every asset's whole history.
| Field | Type | Meaning |
|---|---|---|
| XSEQ | 8S 0 | Transfer sequence (key); the disjoint 20000000+XSEQ ledger range. |
| ASTNO | 8A | Asset moving. |
| FRMCC / TOCC | 4A | From / to cost centre (FRMCC captured by the run). |
| XDT | 8S 0 | Transfer date. |
| XSTAT | 1A | R requested, C complete. |
| XREASN | 25A | Reason text. |
| Field | Type | Meaning |
|---|---|---|
| ASTNO | 8A | Asset disposed (key). |
| DISPDT | 8S 0 | Disposal date. |
| PROCED | 11P 2 | Proceeds (seeded on the request). |
| DCOST / DACCUM / DSPNBV | 11P 2 | Cost / accumulated / NBV snapped by the run. |
| GAINLS | 11P 2 | Gain (+) or loss (−) = proceeds − NBV. |
| DSTAT | 1A | R requested, C complete. |
| Field | Type | Meaning |
|---|---|---|
| ASTNO / MSEQ | 8A / 3S 0 | Asset + schedule line (key). |
| MDESC | 25A | Service description. |
| NEXTDT / INTDAY | 8S 0 / 4S 0 | Next-due date / interval in days. |
| WARRDT | 8S 0 | Warranty expiry date. |
| MSTAT | 1A | S scheduled, D due (flagged by the weekly sweep). |
| MCOST | 11P 2 | Estimated service cost (accumulated by the sweep). |
| Field | Type | Meaning |
|---|---|---|
| LSEQ | 8S 0 | Ledger sequence (key), derived from a stable business key on a disjoint per-program range. |
| ASTNO | 8A | Asset affected. |
| LTYPE | 1A | C capitalise, X transfer, S sale/disposal, P depreciation, F retirement. |
| LDT | 8S 0 | Ledger date. |
| LAMT | 11P 2 | Amount (signed for gain/loss; NBV for a transfer). |
| LREF / LMEMO | 8A / 25A | Reference / memo. |
LSEQ ranges: capitalise 10000000+digits, transfer 20000000+XSEQ, disposal 30000000+digits, depreciation 40000000+digits (per-period guard is the FADEPH composite key), retirement 60000000+digits.
| Field | Type | Meaning |
|---|---|---|
| RYEAR / ASTNO | 4S 0 / 8A | Register year + asset (key); successive years accumulate. |
| RCCTR | 4A | Cost centre (for the subtotal). |
| RCOST / RACCUM / RNBV | 11P 2 | Cost / accumulated / NBV snapshot. |
| RSTAT | 1A | Status at register time. |
| Column | Type | Meaning |
|---|---|---|
| GLSEQ | DECIMAL(8,0) | Distribution sequence (PK), seeded period×100 + n. |
| GLPER | DECIMAL(6,0) | Period YYYYMM (indexed with ACCT via FAGLDACC). |
| ACCT / CCTR | CHAR(9) / CHAR(4) | GL account / cost centre. |
| DRCR | CHAR(1) | D debit (7100-DEPN expense) / C credit (1590-ADEP accumulated). |
| AMT | DECIMAL(11,2) | Distribution amount. |
| GLREF / GLDT | CHAR(8) / DECIMAL(8,0) | Reference / date. |
Holds the processing period the monthly programs read, created by FASETUP with value
' 202608'. Advance it before each monthly run with
CHGDTAARA DTAARA(ESTATE/FADEPPER) VALUE(' YYYYMM'); a blank/invalid value falls back to
202608 in the programs.
CALL ESTATE/FASETUP (creates objects, compiles programs), then
CALL ESTATE/FAREFLD (loads classes, cost centres, pending assets, maintenance).SBMJOB CMD(CALL PGM(ESTATE/FADAILY)) — capitalises any new
pending asset, actions transfer and disposal requests.CALL ESTATE/FAMENU (option 1 asset inquiry,
option 2 depreciation history).FAWEEK and review the maintenance-due, warranty and
cost-centre lines.Pre-checks: confirm the job's library list includes ESTATE; for transfers/disposals,
confirm the request rows are seeded at status 'R'.
Post-checks after the daily cycle:
FAACQ CAPITALISED= equals the pending assets; each is now 'A' with
INSVDT=ACQDT and NBV=COST; one FALEDG 'C' row each.FAXFRUN TRANSFERRED= equals the valid 'R' requests; refused ones stay 'R' (bad/closed
destination) and the asset is unmoved.FADISPOS DISPOSED= equals the 'R' disposals; each asset is now 'D' with NBV 0 and a
signed FALEDG 'S' row (GAIN/LOSS memo).USEUNIT on
AS300001/AS300002); a unit asset that ran 0 units charges nothing that month.CHGDTAARA DTAARA(ESTATE/FADEPPER) VALUE(' YYYYMM').FAMONTH. Confirm FADEPRUN PER=YYYYMM POSTED=n,
FAGLPST PER=YYYYMM ROWS=2n TOTAL=, and the four FADEPSUM lines.Reconciling figures (the same ones the cycles suite checks against a hand-derived oracle, period 202608):
Ad-hoc GL review:
SELECT ACCT, DRCR, SUM(AMT) FROM ESTATE.FAGLDIST WHERE GLPER = 202608 GROUP BY ACCT, DRCR ORDER BY ACCT;
FAMONTH has run for the year (FADEPH holds each asset's periods).FAYEAR. Confirm FARETIRE RETIRED=, FANBVRL ... CLAMPED=0,
FAREGEN YEAR=y LINES=, FAREGPR LINES=.-- one row per live asset for the year; RACCUM/RNBV are the year-end position SELECT ASTNO, RCCTR, RCOST, RACCUM, RNBV FROM ESTATE.FAREG WHERE RYEAR = 2026;
'F' with NBV 0 and a FALEDG 'F' row (e.g. AS400001 at LSEQ 60400001).CLAMPED=0 means no asset was out of line; a non-zero count means
the roll had to hold accumulated at the base or NBV at 0 (investigate the source posting).Each program DSPLYs a counted result line. Every posting program is guarded so a re-run posts nothing rather than double-posting.
| Situation | Behaviour | Action |
|---|---|---|
| Re-run the daily cycle | Capitalise/transfer/dispose all report their count as 0 on the second pass. | Safe: stable ledger keys + status guards make the whole daily chain re-runnable. The ledger does not grow. |
| Re-run depreciation for the same period | POSTED=0 SKIP=n; accumulated unchanged. | Safe no-op — composite (ASTNO, DPER) guard. Advance FADEPPER to post a new month. |
| Re-run the GL distribution | Reports ALREADY POSTED, ROWS=0. | Safe: FAGLPST checks for existing period rows first. |
| Depreciation reaches the base | The final charge is CLAMPED; a fully-depreciated asset is SKIPPED (no row). | Expected. NBV can never go below salvage or negative. |
| Transfer to bad/closed cost centre | SKIP incremented; request stays 'R'; asset unmoved. | Fix the destination (or open the cost centre) and re-run. |
| Re-run the annual cycle | Retires/registers nothing a second time; FAREG holds the same rows. | Safe: 60000000+digits ledger key and (RYEAR, ASTNO) register key are the guards. |
| Units-of-production asset shows no charge | USEUNIT was 0 for the period. | Set USEUNIT before the monthly run; a unit asset only depreciates on units used. |
The mechanics that matter when maintaining ESTATE/i, from src/sources.mjs. All objects are
in library ESTATE.
All three methods are computed per PERIOD (one month) in FADEPRUN, subroutine
DEP1, and every method half-adjusts to the cent (eval(h)) rather than
truncating — a ledger that truncated every month would drift cents from the base and the final clamp
would carry the whole error.
monthly = (COST - SALVAGE) / LIFEMT (only when LIFEMT > 0). Flat across
periods.monthly = (COST - ACCDEP) × DRATE / 12. The rate is applied to net book value, so
the charge shrinks each month (classic reducing balance).monthly = USEUNIT × DRATE (units run in the period × per-unit rate). The
harness/operator stamps USEUNIT before the run; the program consumes it.The invariant, enforced for every method: the depreciable base is
WBASE = COST - SALVAG and the remaining base is WREM = WBASE - ACCDEP. If the
computed charge exceeds WREM it is clamped to WREM; a charge ≤ 0
skips the asset (no history row). So accumulated depreciation can never exceed COST - SALVAGE
and NBV can never fall below salvage while the asset is live, nor go negative. The annual
FANBVRL re-asserts this by clamping accumulated to the base and NBV to non-negative and
counting how many it had to fix.
Every posting program derives its key from a stable business key on a disjoint LSEQ range,
then CHAIN(EN)s that key before writing: if the row exists it increments SKIP and leaves;
otherwise it writes. A re-run regenerates the identical key and is refused rather than double-posting.
| Program | Key | Guard |
|---|---|---|
| FAACQ | FALEDG 10000000 + %dec(%subst(astno:3:6):8:0) | Stable ledger key + ASTAT='P' check. |
| FAXFRUN | FALEDG 20000000 + XSEQ | Stable ledger key + XSTAT='R' + open destination. |
| FADISPOS | FALEDG 30000000 + asset digits | Stable ledger key + DSTAT='R' + not already 'D'. |
| FADEPRUN | FADEPH composite (ASTNO, DPER) | Per-period guard via a KLIST CHAIN — the app's most important control. |
| FAGLPST | FAGLDIST COUNT(*) WHERE GLPER = :wper | Existing-period check → ALREADY POSTED. |
| FARETIRE | FALEDG 60000000 + asset digits | Stable ledger key + ASTAT='A' + base reached. |
| FAREGEN | FAREG composite (RYEAR, ASTNO) | Per-year guard via a KLIST CHAIN. |
Two RPG idioms recur and are worth understanding before editing:
CHAINs
other files (the class, the ledger). Those chains move the file cursor off the driver record, so
before it updates the driver the program re-CHAINs the driver on its captured key (e.g.
WAST CHAIN(E) FAASSETR). Omitting the re-CHAIN would update whatever record the cursor
last landed on.FADEPH (ASTNO, DPER),
FAMAINT (ASTNO, MSEQ), FAREG (RYEAR, ASTNO) — the guard/re-CHAIN uses a
KLIST of both parts. Chaining on the first part alone would match the asset's FIRST period
(or first schedule line) and wrongly skip or update the wrong row; both key fields must be in the
KLIST. This is called out explicitly in the FADEPRUN and FAMTSWP source.Embedded SQL (FAGLPST). The one SQLRPGLE program uses free-form exec sql: a
SELECT COUNT(*) INTO :wexist idempotency check, then an INSERT INTO ESTATE.FAGLDIST
VALUES(...) per DR/CR row inside a begsr glrow subroutine, summing the period read
through the period-keyed logical FADEPHR (accessed as FADEPLF).
ESTATE/i signals no application SQLSTATEs; every program reports its outcome as a counted DSPLY line. The completion banners are sent by the CL cycles.
| Line | Program | Meaning |
|---|---|---|
| FAACQ CAPITALISED=n SKIP=n | FAACQ | n assets capitalised; SKIP = already-capitalised or not pending. |
| FAXFRUN TRANSFERRED=n SKIP=n | FAXFRUN | n transfers actioned; SKIP = not-'R'/bad destination/already posted. |
| FADISPOS DISPOSED=n SKIP=n | FADISPOS | n disposals actioned; SKIP = not-'R'/already 'D'/already posted. |
| FAMTSWP DUE=n SKIP=n COST=n | FAMTSWP | n schedules flagged due; total estimated cost. |
| FAWARN EXPIRED=n INWARR=n | FAWARN | n warranties expired / still in warranty (expired assets named). |
| FACCRPT cc N=n COST=n NBV=n | FACCRPT | Per cost centre + CENTRES DONE ASSETS=n. |
| FADEPRUN PER=p POSTED=n SKIP=n | FADEPRUN | n assets depreciated for period p; SKIP = not-'A'/no base/already posted. |
| FAGLPST PER=p ROWS=n TOTAL=n | FAGLPST | n distribution rows / total; or ROWS=0 ALREADY POSTED. |
| FADEPSUM STRAIGHT/DECLINE/UNITS/TOTAL/ROWS | FADEPSUM | COBOL period totals by method (independent proof). |
| FARETIRE RETIRED=n SKIP=n | FARETIRE | n fully-depreciated assets retired. |
| FANBVRL ASSETS=n NBV=n CLAMPED=n | FANBVRL | n assets rolled; CLAMPED = invariant fixes applied. |
| FAREGEN YEAR=y LINES=n NBV=n SKIP=n | FAREGEN | n register rows written for year y. |
| FAREGPR LINES=n NBV=n | FAREGPR | n register lines spooled. |
COST - ACCDEP.ASTAT P→A and posting a FALEDG 'C' row (FAACQ).(COST - ACCDEP) × DRATE / 12.COST - SALVAGE — the ceiling accumulated depreciation may never exceed. The invariant
clamps every charge to what is left of it.COST - ACCDEP. Derived, and also carried on the master and on
the inquiry screen; never below salvage while live, never negative.SBMJOB CMD(CALL PGM(ESTATE/FADAILY))).(COST - SALVAGE) / LIFEMT.USEUNIT × DRATE (units run this period × per-unit rate);
no usage, no charge.