Depreciation Completeness — reverse runs, reject dead methods, retire DRAFT¶
Date: 2026-06-27 Status: shipped Audit findings: #24, #25, #26 — depreciation run statuses and the units-of-production method were decorative or unreachable.
Problem / motivation¶
Three depreciation gaps:
- #24
DepreciationRunStatus.REVERSEDwas never produced — there was no way to reverse a posted depreciation run. - #25
DepreciationMethod.UNITS_OF_PRODUCTIONalways computed0, so an asset registered with it silently never depreciated. - #26
DepreciationRunStatus.DRAFTwas vestigial — a zero-depreciation run was left as a misleading DRAFT that no calculation could see.
What shipped¶
- Reverse a depreciation run (#24): new
reverseDepreciationRunmutation →FixedAssetService.reverse_run. It reverses the run's GL entry (undoing the expense and accumulated depreciation), marks the runREVERSED, and returns any asset it had fully depreciated toACTIVE. Because net book value and accumulated depreciation only countPOSTEDruns, the asset's book value is restored automatically. Reversing a non-posted run is rejected. - Reject units-of-production at registration (#25):
registernow raises a clearValidationErrorforUNITS_OF_PRODUCTIONinstead of booking an asset that would never depreciate. (Implementing it needs per-period usage data the system doesn't capture — see Future additions.) - No more vestigial DRAFT (#26):
run_periodnow always marks the runPOSTED— even an empty period (no depreciable assets) — so the period is closed and the duplicate-run guard (audit #27) covers it. - Consistency fix:
_accumulated_depreciation(used by disposal) now counts onlyPOSTEDruns, matching net-book-value, so a reversed run is fully undone for disposal too.
Files¶
- fixed_asset_service.py —
reverse_run, UoP guard, always-POSTED runs, accumulated-depreciation fix. - fixed_asset_mutations.py —
reverse_depreciation_runmutation. - tests/graphql/fixed_assets/test_depreciation_idempotency.py — reverse nets to zero; re-reverse rejected; UoP rejected.
GraphQL surface¶
- New mutation
reverseDepreciationRun(runId: UUID!): DepreciationRun— additive, gated by the existingFIXED_ASSETSpermission.
Frontend impact¶
- A new "reverse depreciation run" action is available. Registering an asset with the units-of-production method now returns a validation error (previously it succeeded but the asset never depreciated).
Future additions¶
- Units-of-production depreciation: implement it once per-period usage/output is captured (a usage table + a per-period usage input on the run), then drop the registration guard.
- Reversed-run book value across multiple runs:
reverse_runre-activates a fully-depreciated asset whenever it had a line in the reversed run; a multi-run asset that is still fully depreciated by other runs would be a rare edge case to refine.