Fixed assets¶
Asset register, depreciation schedules, disposal handling, and the plumbing into the general ledger. Designed around the way Australian tax law treats capitalised items, but the underlying model is generic enough to drive other jurisdictions.
Concepts¶
- Asset — a single capitalised item with a cost, in-service date, depreciation model, and three paired GL accounts (cost, accumulated depreciation, depreciation expense). Physical tracking fields (serial number, location, custody person, manufacturer, warranty) are optional.
- Depreciation model — a row in the
depreciation_modelscatalogue (keyed by slug, e.g.asset_5_year_linear,asset_dv_30) that declares the method and rate. Every asset references one book model; an optional secondtax_model_idprovides a separate tax-depreciation overlay for reporting (never posted to GL). - Depreciation — compute-on-demand. There is no stored schedule
table. The engine derives "amount owed through date X" from the
asset's cost, residual value, in-service date, and model, then
subtracts what has already been posted (tracked by the
last_depreciation_posted_throughcursor on the asset row). Re-running with the same through-date is a guaranteed no-op. - Posting —
post_depreciation()insaebooks.services.assetsposts a Dr Depreciation Expense / Cr Accumulated Depreciation journal per asset and advances the cursor. - Disposal —
dispose_asset()first catches depreciation up to the disposal date, then posts the closeout journal (Dr cash/bank + Dr accumulated depreciation / Cr cost + Cr/Dr gain or loss). A partial disposal (dispose_partial()) splits the asset into parent (reduced cost, stays active) and child (disposed fraction) rows, then runs the full disposal against the child. - Convert to inventory —
convert_to_inventory()handles items that leave the fixed-asset register and become stock.
Depreciation methods¶
| Model slug | Method | Notes |
|---|---|---|
asset_no_depreciation |
no_depreciation |
Land, non-depreciating assets; always yields $0 |
asset_3_year_linear |
linear |
Straight-line over 3 years; (cost − residual) / life |
asset_4_year_linear |
linear |
Straight-line over 4 years |
asset_5_year_linear |
linear |
Straight-line over 5 years |
asset_10_year_linear |
linear |
Straight-line over 10 years |
asset_20_year_linear |
linear |
Straight-line over 20 years |
asset_dv_40 |
diminishing_value |
40% per annum DV; book_value × rate / 12 per month |
asset_dv_30 |
diminishing_value |
30% per annum DV |
asset_dv_20 |
diminishing_value |
20% per annum DV |
asset_dv_15 |
diminishing_value |
15% per annum DV |
asset_dv_10 |
diminishing_value |
10% per annum DV |
asset_dv_5 |
diminishing_value |
5% per annum DV |
Linear depreciation uses day-count proration (365.25/12 ≈ 30.4375
days/month). Diminishing-value steps month-by-month, prorating the
first and last months, and caps at cost − residual so book value
asymptotes to salvage without overshooting.
Method-and-rate calculation lives in saebooks.services.assets
(_cumulative_linear / _cumulative_dv). New methods are added by
extending the _cumulative_for_model dispatch in that module.
Posting model¶
A typical asset's lifecycle journals look like:
| Event | Dr account | Cr account |
|---|---|---|
| Acquisition | Asset (cost_account_id) |
Bank / Trade payables |
| Depreciation | Depreciation expense | Accumulated depreciation |
| Disposal — sale | Bank (proceeds) + Accum dep | Asset cost + Gain on disposal |
| Disposal — scrap | Accum dep + Loss on disposal | Asset cost |
The three GL accounts are specified per asset at creation:
cost_account_id, accum_dep_account_id, and dep_expense_account_id
(all three are required in the API; the CSV import path defaults
dep_expense_account_code to 6-1500 Depreciation Expense when the
column is omitted).
Disposal journals carry origin = DEPRECIATION for the catch-up step
and origin = FIXED_ASSET for the final closeout. Partial disposals
split the asset row and dispose only the child fraction.
ATO compliance¶
Effective lives are represented through the depreciation model catalogue. Override the model on the asset to use a self-determined life — the asset's change log records the override and the user who applied it.
Tax depreciation (e.g. for schedules that differ from book) can be
tracked via the optional tax_model_id on the asset. This is a
reporting-only overlay and never drives GL postings — it is used solely
to produce the book-vs-tax temporary difference column on the
depreciation schedule report.
Reports¶
| Report | Endpoint |
|---|---|
| Depreciation schedule (all assets) | GET /api/v1/reports/depreciation_schedule |
The depreciation schedule report lists all active (non-disposed,
non-archived) assets with their cost, accumulated depreciation,
net book value, and tax written-down value (when a tax_model_id is
set) as at a given as_of_date (defaults to today). Output is JSON;
CSV download is available via the web router.
The report also accepts a method filter (STRAIGHT_LINE,
DECLINING_BALANCE, linear, diminishing_value).
Bulk operations¶
Importing an existing asset register (from a migration off Xero / MYOB / QuickBooks) is a common day-one task. The import is a two-step parse + apply flow (same pattern as the chart-of-accounts import):
Required CSV columns: code, name, purchase_date, cost,
depreciation_model_id, cost_account_code, accum_dep_account_code
Optional CSV columns: in_service_date, residual_value,
dep_expense_account_code, description, serial_number,
manufacturer, model_number, location, custody_person,
warranty_end
depreciation_model_id is the catalogue slug (e.g.
asset_5_year_linear, asset_dv_30). Account codes are the
hyphenated form stored in the DB (e.g. 1-3310).
The import is idempotent on (company_id, code) — re-running the
same CSV twice adds zero new rows. Rows with an existing code are
classified as skip; parse/resolution failures are classified as
invalid and reported line-by-line without blocking valid rows.
The apply step commits all create rows atomically.