Skip to content

PP&E movement note (HKFRS s.17) doesn't foot — double-counts mid-period acquisitions + phantom depreciation on disposals

Why (owner, 2026-06-30)

"Can you help me check whether all tabs under the accounting page (especially the components that helps build the WPP) are structurally sound, and to further check whether it produces documentations with accurate information from accurate data"

The Working Paper Pack and the Notes tab both render an auto-generated property-plant-and-equipment movement schedule. It failed to foot (opening + additions − depreciation − disposals ≠ closing) in the common cases.

Diagnosis

buildPpeMovementAutoNote (lib/accounting/notes.server.ts) reads the Fixed Asset Register at two snapshots and aggregated the movement inline. The register (listFixedAssetsServer) computes a row for every asset via computeAsOf, regardless of acquisition/disposal date — and computeAsOf returns netBookValue = cost for an asset whose acquisition date is after the as-of date (months-elapsed 0), and keeps accruing depreciation past the disposal date. So:

  • Mid-period acquisition (common): the asset appears in the opening snapshot with NBV = full cost, so the old loop added the full cost to opening NBV AND to additions — double counted. A $36,000 July purchase showed opening 36,000 + additions 36,000 − dep 6,750 = 65,250 vs closing 29,250.
  • In-period disposal: depreciation was taken to year-end (not clamped at the disposal month), and the removed carrying amount was approximated by opening NBV → the row didn't foot.
  • Prior-period disposal: the asset still produced a positive depreciation delta (phantom depreciation for an asset already gone).

Only the steady-state "held all year, no disposal" case reconciled.

What shipped

  • New pure, unit-tested buildPpeMovementRows(opening, closing, window) in lib/accounting/fixedAssets/types.ts (matches the codebase's pure-core + I/O-wrapper pattern). It decides membership by each asset's real dates:
  • opening NBV only for assets in service at the opening date (acquisitionDate <= openingIso, not disposed by then);
  • additions = cost of assets acquired in the period;
  • depreciation clamped to the disposal month for in-period disposals (via computeAsOf(asset, disposalDate));
  • disposals = carrying amount removed = cost − accumDep@disposal (floored at residual);
  • assets disposed before the period are skipped entirely.
  • notes.server.ts now calls the helper instead of the inline loop; rendering unchanged.
  • Test __tests__/lib/accounting/ppeMovement.test.ts proves the schedule foots for mid-period acquisition, held-all-year, in-period disposal, prior-period disposal, and a mixed portfolio.

This makes the note internally foot. Two adjacent fixed-asset findings from the same audit are separate and still open:

  1. Disposals never post a journal entrydisposeFixedAssetServer records the disposal but never removes cost + accumulated depreciation from the GLs (no gain/loss), so the Balance Sheet fixed-asset line and this note still won't reconcile to each other until that's fixed. (Needs an owner decision on the gain/loss GL.)
  2. computeAsOf month math is timezone-fragile and differs from the depreciation journal generator's month math — a latent reconciliation gap.

Both to be tracked/fixed separately.

Verification

  • Attestation (Accounting (Diagnostics)): read AGENTS.md; checked the board by scope, not UID — no task covered the PP&E movement-note footing bug. Tracking T-142.
  • Source: Accounting (Diagnostics) · https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea
  • Found via a parallel read-only audit of the WPP/reports pipeline; verified against current main (the recent WPP rework was presentation-only and left the data layer unchanged).
  • NODE_OPTIONS=--max-old-space-size=4096 npx tsc --noEmit clean; npx vitest run 527/527.

Blast Radius

The PP&E movement schedule in the WPP and the Notes tab. No change to the Fixed Asset Register totals, the depreciation journal, or any stored data — only the movement-note aggregation, which now foots.

Commit index (backfilled 2026-07-01, best-effort · Coaching (Diagnostic))

Candidate related commits, auto-backfilled from git on main: commits whose message references this task's UID or a PR number it cites. Not verified — this squash-merged history can't yield a precise per-task list, so rows tagged (mentions only) name the task in passing (may be tangential) and untagged work commits may be missing. Treat as a starting point: verify, prune tangential rows, and append any real ones per the AGENTS.md "record every related SHA" policy.

  • f87dce5 2026-06-30 — fix(accounting): PP&E movement note now foots — no mid-period-acquisition double-count or phantom depreciation (T-142)