Modul Aset Tetap ERP: Jadwal Penyusutan yang Benar

Foto oleh Loco Steve on flickr
Penyusutan garis lurus membagi basis penyusutan, yaitu biaya perolehan dikurangi nilai sisa, secara rata sepanjang umur manfaat sehingga setiap periode mendapat beban yang sama. Penyusutan saldo menurun menerapkan tarif tetap terhadap nilai buku aset yang tersisa, sehingga beban paling besar di awal dan mengecil seiring waktu. Kedua metode harus berhenti mengurangi nilai buku begitu mencapai nilai sisa aset.
Register aset harus mencatat biaya perolehan, nilai sisa, umur manfaat, dan metode penyusutan pada saat aset diperoleh, karena semua perhitungan berikutnya bergantung pada nilai-nilai itu tetap akurat. Daftar barang generik tidak memaksakan hal ini, sehingga jadwal penyusutan bisa diam-diam melenceng dari yang diharapkan tim finance. Register ini pada dasarnya adalah system of record yang dibaca oleh proses posting bulanan.
Setiap proses posting harus menghitung beban periode per aset, lalu memposting satu entri jurnal yang seimbang, mendebit beban penyusutan dan mengkredit akumulasi penyusutan, dikelompokkan di bawah satu identifier run. Proses ini harus idempotent dan menyimpan nilai buku terbaru setiap aset agar periode berikutnya mulai dari angka yang benar. Ini menjaga subledger aset tetap tetap rekonsiliasi dengan general ledger setiap saat.
Bisa, dan kebanyakan perusahaan nyata memang melakukan ini, memakai garis lurus untuk furnitur dan peralatan kantor sambil menerapkan saldo menurun untuk kendaraan dan mesin. Metode penyusutan sebaiknya menjadi field di setiap record aset, bukan setting sistem global, agar job batch bulanan bisa mengarahkan ke perhitungan yang tepat per aset tanpa perlu kasus khusus untuk setiap kategori.
Pelepasan harus memicu entri penyusutan parsial terakhir sampai tanggal pelepasan, lalu entri jurnal terpisah yang menihilkan akun biaya perolehan dan akumulasi penyusutan aset sambil mengakui laba atau rugi dibandingkan hasil pelepasan. Baris aset sebaiknya ditandai sebagai sudah dilepas, bukan dihapus, agar laporan penyusutan historis dan audit tetap berfungsi dengan benar.

Foto oleh Loco Steve on flickr
Setiap ERP pada akhirnya membutuhkan modul aset tetap, dan kebanyakan tim meremehkannya. Kelihatannya seperti layar CRUD biasa untuk laptop dan mesin, sampai tim finance meminta jadwal penyusutan yang harus rekonsiliasi dengan general ledger setiap bulan, selamanya, tanpa penyesuaian manual.
Tulisan ini membahas rancangan yang saya pakai untuk modul aset tetap di dalam ERP custom: skema register aset, perbedaan sebenarnya antara penyusutan garis lurus dan saldo menurun dalam kode, cara pelepasan aset menutup catatan dengan bersih, dan cara seluruh proses ini di-posting ke general ledger sebagai batch bulanan yang berulang, bukan ritual spreadsheet.
Sebelum perhitungan penyusutan apa pun berjalan, kita butuh register yang mencatat fakta yang benar-benar dipedulikan tim finance saat aset diperoleh. Kalau bagian ini salah, semua perhitungan berikutnya ikut mewarisi kesalahan tersebut. Register bukan sekadar daftar barang fisik; ia adalah system of record untuk basis biaya, asumsi umur manfaat, dan kebijakan penyusutan yang diterapkan pada setiap aset.
| Field | Tipe | Catatan |
|---|---|---|
| acquisition_cost | NUMERIC(15,2) | Basis biaya yang dikapitalisasi, dipakai di setiap rumus penyusutan selanjutnya |
| salvage_value | NUMERIC(15,2) | Nilai buku tidak pernah disusutkan sampai di bawah batas ini |
| useful_life_months | INTEGER | Disimpan dalam bulan agar akuisisi di tengah tahun tidak butuh penanganan khusus |
| depreciation_method | TEXT | Berbasis enum, menentukan fungsi perhitungan mana yang jalan saat batch bulanan |
Tim finance memilih metode penyusutan karena alasan pajak atau pelaporan, tapi modul harus mengimplementasikan kedua metode dengan benar dan membiarkan setiap aset memakai metodenya sendiri. Kedua metode berbeda dari basis perhitungannya dan seberapa rata kurva bebannya sepanjang waktu. Menentukan abstraksi ini dengan benar sejak awal lebih penting dari yang terlihat, karena menambahkan metode kedua ke modul yang sudah dipatok hanya untuk satu metode adalah migrasi yang menyakitkan begitu ratusan aset sudah punya riwayat posting.
Penyusutan garis lurus membagi basis penyusutan, yaitu biaya perolehan dikurangi nilai sisa, secara rata sepanjang umur manfaat. Setiap periode mendapat beban yang persis sama sampai aset mencapai nilai sisanya. Ini metode default yang dipakai kebanyakan perusahaan untuk peralatan kantor dan furnitur karena mudah diaudit dan sesuai dengan cara aset itu benar-benar dipakai.
Penyusutan saldo menurun menerapkan tarif persentase tetap terhadap nilai buku aset yang tersisa, bukan biaya aslinya, sehingga beban paling besar di periode-periode awal dan mengecil seiring waktu. Ini cocok untuk kendaraan, komputer, dan mesin yang kehilangan sebagian besar nilainya di beberapa tahun pertama pemakaian. Detail implementasi yang krusial adalah nilai buku tidak boleh sampai di bawah nilai sisa, artinya beban yang dihitung setiap periode harus dibatasi terhadap sisa jumlah yang masih bisa disusutkan.
// Straight-line: equal expense every period
function straightLineMonthly(asset: FixedAsset): number {
const depreciableBase = asset.acquisitionCost - asset.salvageValue
return depreciableBase / asset.usefulLifeMonths
}
// Declining-balance: fixed rate against the remaining book value
function decliningBalanceMonthly(asset: FixedAsset, bookValue: number): number {
const annualRate = asset.decliningRate ?? 2 / (asset.usefulLifeMonths / 12) // double-declining default
const monthlyRate = annualRate / 12
const rawExpense = bookValue * monthlyRate
// never depreciate below salvage value
const floor = bookValue - asset.salvageValue
return Math.min(rawExpense, Math.max(floor, 0))
}
function computeMonthlyDepreciation(asset: FixedAsset, bookValue: number): number {
switch (asset.depreciationMethod) {
case 'STRAIGHT_LINE':
return straightLineMonthly(asset)
case 'DECLINING_BALANCE':
return decliningBalanceMonthly(asset, bookValue)
default:
throw new Error(`Unsupported method: ${asset.depreciationMethod}`)
}
}Simpan metode penyusutan sebagai enum di baris aset itu sendiri, bukan sebagai setting sistem global. Perusahaan nyata sering mencampur garis lurus untuk furnitur dengan saldo menurun untuk kendaraan dalam register aset yang sama, dan job batch bulanan Anda harus mengarahkan ke rumus yang tepat per aset tanpa perlu kasus khusus untuk setiap kategori.
Bagian yang benar-benar terbukti berguna di produksi adalah proses posting bulanan. Tim finance harus bisa memicu satu aksi yang menghitung penyusutan setiap aset aktif untuk periode tersebut, menulis jadwalnya, dan memposting satu batch entri jurnal yang seimbang ke general ledger. Proses ini harus idempotent dan bisa diaudit, karena suatu saat pasti ada yang bertanya mengapa nilai buku sebuah aset berubah di bulan Maret. Perlakukan identifier run sebagai record utama, bukan sekadar foreign key, karena itulah yang memungkinkan tim finance membalik seluruh posting satu periode sebagai satu kesatuan jika belakangan ditemukan kesalahan.
-- one journal entry per asset per period, batched into a single GL batch
CREATE TABLE depreciation_runs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
period_month DATE NOT NULL, -- first day of the accounting period
status TEXT NOT NULL DEFAULT 'DRAFT', -- DRAFT|POSTED|REVERSED
posted_at TIMESTAMPTZ,
UNIQUE (period_month)
);
CREATE TABLE depreciation_entries (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
run_id UUID NOT NULL REFERENCES depreciation_runs(id),
asset_id UUID NOT NULL REFERENCES fixed_assets(id),
expense_amount NUMERIC(15,2) NOT NULL,
book_value_after NUMERIC(15,2) NOT NULL,
gl_journal_id UUID REFERENCES gl_journals(id)
);
// NestJS: close the period by posting one balanced journal per asset
async function postDepreciationRun(periodMonth: Date) {
const run = await this.runRepo.create({ periodMonth, status: 'DRAFT' })
const assets = await this.assetRepo.findActive()
for (const asset of assets) {
const expense = computeMonthlyDepreciation(asset, asset.currentBookValue)
if (expense <= 0) continue
const journal = await this.glService.postJournal({
date: periodMonth,
lines: [
{ account: 'DEPRECIATION_EXPENSE', debit: expense, credit: 0 },
{ account: 'ACCUMULATED_DEPRECIATION', debit: 0, credit: expense },
],
memo: `Depreciation ${asset.code} ${periodMonth.toISOString().slice(0, 7)}`,
})
await this.entryRepo.create({
runId: run.id,
assetId: asset.id,
expenseAmount: expense,
bookValueAfter: asset.currentBookValue - expense,
glJournalId: journal.id,
})
asset.currentBookValue -= expense
await this.assetRepo.save(asset)
}
run.status = 'POSTED'
run.postedAt = new Date()
await this.runRepo.save(run)
}Jangan pernah membiarkan proses posting bulanan menghitung ulang dari nol hanya menggunakan biaya perolehan asli. Jika proses tersebut membaca nilai buku yang basi atau dihitung ulang, bukan nilai yang disimpan dari periode terakhir yang sudah diposting, satu bulan yang terlewat saja bisa diam-diam merusak semua perhitungan saldo menurun berikutnya untuk aset itu, dan kesalahan ini mudah terlewat sampai auditor merekonsiliasi akun akumulasi penyusutan.
Pelepasan aset adalah titik di mana modul aset tetap yang dibangun sekadar tambahan akhirnya berantakan. Menjual, memusnahkan, atau menghapusbukukan aset di tengah periode bukan sekadar mengubah status; ini adalah peristiwa finansial yang butuh entri jurnal sendiri untuk mengakui laba atau rugi antara hasil pelepasan dan sisa nilai buku aset. Jika modul tidak bisa merepresentasikan beban penyusutan parsial bersamaan dengan entri pelepasan dalam satu transaksi yang sama, tim finance akhirnya menghitung manual dan menambahkan penyesuaian belakangan, yang menggagalkan seluruh tujuan otomatisasi modul ini sejak awal.
Setelah pelepasan berhasil menihilkan akun biaya perolehan dan akumulasi penyusutan aset tersebut dalam entri jurnal yang sama, subledger aset tetap Anda akan selalu cocok dengan akun kontrol di general ledger, yang merupakan properti paling berguna yang bisa diminta tim finance dari sebuah modul ERP.
Modul aset tetap adalah sebuah subledger, dan subledger hanya layak dipercaya kalau bisa direkonsiliasi. Total akumulasi penyusutan dari semua aset di register harus sama dengan saldo akun kontrol akumulasi penyusutan di general ledger di titik waktu mana pun. Bangun laporan rekonsiliasi sejak awal, bukan belakangan, karena ini cara tercepat menemukan bug di proses posting bulanan sebelum tim finance menemukannya sendiri.
Sediakan tampilan rekonsiliasi yang membandingkan total nilai buku aset di register dengan saldo akun kontrol aset tetap di general ledger, diperbarui setiap kali setelah proses posting berjalan. Ketika kedua angka itu cocok secara otomatis, tim finance berhenti mengecek spreadsheet secara manual, dan biasanya itulah saat mereka cukup percaya pada modul ini hingga berhenti meminta proses lama sebagai cadangan.
Modul aset tetap yang dirancang dengan benar mengubah penyusutan dari kerja spreadsheet bulanan menjadi proses batch yang berulang dan bisa diaudit. Register aset mencatat fakta yang tepat sekali saja, logika perhitungan mengarahkan dengan benar antara garis lurus dan saldo menurun per aset, pelepasan menutup pembukuan dengan bersih, dan general ledger selalu mencerminkan kondisi terkini dari setiap aset yang dimiliki perusahaan.