Skip to content

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_models catalogue (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 second tax_model_id provides 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_through cursor on the asset row). Re-running with the same through-date is a guaranteed no-op.
  • Posting — post_depreciation() in saebooks.services.assets posts 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.