Aplikasi Keuangan VB.NET: Catatan Keuangan Pribadi dan UMKM dari Nol

Aplikasi keuangan VB.NET SC Keuangan: layar utama, potongan kode Model.vb, dan ringkasan selisih bulanan

Pernah merasa gaji baru masuk, tapi pertengahan bulan dompet sudah tipis tanpa tahu uangnya pergi ke mana? Masalah yang sama juga sering menimpa pemilik warung atau kedai kecil, karena uang usaha dan uang pribadi bercampur di satu laci. Solusinya sebenarnya sederhana, yaitu mencatat setiap uang masuk dan keluar, lalu melihat polanya setiap bulan. Pada tutorial ini kita membangun aplikasi keuangan VB.NET bernama SC Keuangan dari nol dengan Windows Forms dan .NET 8, tanpa database dan tanpa paket NuGet.

Hasil akhirnya berupa satu layar utama yang berisi form input catatan, kartu ringkasan pemasukan, pengeluaran, dan selisih, tabel catatan per bulan, serta grafik batang pengeluaran per kategori. Selain itu, aplikasi ini bisa mengekspor data bulanan ke CSV agar Anda bisa membukanya di Excel. Source code lengkapnya gratis, jadi Anda bisa langsung download SC Keuangan VB.NET di sharecode.id lalu mengikuti tutorial ini sambil membuka project aslinya.

Tampilan form utama aplikasi keuangan VB.NET SC Keuangan dengan kartu ringkasan, tabel catatan, dan grafik pengeluaran
Layar utama SC Keuangan saat pertama kali jalan, lengkap dengan data contoh bulan berjalan.

Fitur yang Akan Kita Bangun

Sebelum menulis kode, mari kita sepakati dulu daftar fiturnya. Dengan begitu, setiap langkah nanti punya tujuan yang jelas.

  • Pencatatan pemasukan dan pengeluaran lengkap dengan tanggal, kategori, keterangan, dan jumlah.
  • Kategori bawaan per jenis, plus kategori baru yang cukup Anda ketik langsung di combo box.
  • Input nominal yang otomatis memberi titik ribuan, misalnya 65000 menjadi 65.000.
  • Filter bulan memakai tombol <, >, dan Bulan Ini.
  • Tiga kartu ringkasan bulanan, plus saldo seluruhnya di header.
  • Grafik batang pengeluaran per kategori buatan sendiri dengan GDI+, tanpa library chart.
  • Klik baris tabel untuk mengubah atau menghapus catatan, selalu dengan konfirmasi.
  • Ekspor CSV per bulan dengan pemisah titik koma yang cocok untuk Excel versi Indonesia.
  • Semua data tersimpan sebagai file JSON di folder data samping file .exe.

Persiapan: Visual Studio 2022 dan .NET 8

Pertama, pastikan Anda memakai Visual Studio 2022 versi 17.8 atau lebih baru dengan workload .NET desktop development. Versi ini penting karena Visual Studio 2019 tidak mengenal .NET 8. Anda bisa mengecek versinya lewat menu Help → About Microsoft Visual Studio. Sebagai alternatif, cukup pasang .NET 8 SDK jika Anda lebih suka build lewat command line.

Membuat Project Windows Forms VB

  1. Buka Visual Studio 2022, lalu pilih Create a new project.
  2. Ketik “Windows Forms” di kotak pencarian, kemudian pilih template Windows Forms App yang berlabel Visual Basic (bukan C#, dan bukan versi .NET Framework).
  3. Isi nama project SCKeuangan beserta nama solution yang sama, setelah itu klik Next.
  4. Pilih framework .NET 8.0 (Long Term Support), lalu klik Create.
  5. Hapus Form1.vb bawaan, kemudian buat folder Forms dan tambahkan form baru bernama FormUtama.

Setelah itu, tambahkan file class kosong Program.vb, Model.vb, Penyimpanan.vb, Repo.vb, Util.vb, dan PanelGrafik.vb. Isinya akan kita lengkapi satu per satu pada langkah berikutnya.

Mengatur File Project (.vbproj)

Selanjutnya, klik dua kali nama project di Solution Explorer untuk membuka file .vbproj. Samakan isinya dengan potongan berikut.

<PropertyGroup>
  <OutputType>WinExe</OutputType>
  <TargetFramework>net8.0-windows</TargetFramework>
  <UseWindowsForms>true</UseWindowsForms>
  <RootNamespace>SCKeuangan</RootNamespace>
  <AssemblyName>SCKeuangan</AssemblyName>
  <StartupObject>SCKeuangan.Program</StartupObject>
  <MyType>WindowsFormsWithCustomSubMain</MyType>
  <OptionExplicit>On</OptionExplicit>
  <OptionStrict>On</OptionStrict>
  <OptionInfer>On</OptionInfer>
</PropertyGroup>

Dua baris paling penting adalah StartupObject dan MyType. Keduanya membuat aplikasi mulai dari Sub Main milik kita sendiri, bukan dari Application Framework bawaan VB. Selain itu, OptionStrict On memaksa konversi tipe yang eksplisit, sehingga kesalahan seperti menjumlahkan teks dengan angka langsung tertangkap saat build.

Struktur Folder dan File

Struktur akhir project terlihat seperti ini.

SCKeuangan.sln
SCKeuangan/
├── SCKeuangan.vbproj
├── Program.vb
├── Model.vb
├── Repo.vb
├── Penyimpanan.vb
├── Util.vb
├── PanelGrafik.vb
└── Forms/
    ├── FormUtama.vb
    └── FormUtama.Designer.vb
FileFungsi
Program.vbTitik masuk aplikasi, mengatur budaya id-ID dan penanganan error global.
Model.vbClass Catatan untuk satu transaksi.
Repo.vbMenampung data di memori, kategori bawaan, filter bulan, saldo, dan data contoh.
Penyimpanan.vbMembaca dan menulis file JSON.
Util.vbWarna tema, format Rupiah, gaya DataGridView, input nominal, dan helper CSV.
PanelGrafik.vbKontrol grafik batang pengeluaran per kategori.
FormUtama.vbLogika layar utama, mulai dari simpan, ubah, hapus, sampai ekspor.
FormUtama.Designer.vbTata letak kontrol hasil Form Designer.

Arsitektur Aplikasi Keuangan VB.NET

Aplikasi ini sengaja memakai arsitektur berlapis yang ringan. Form tidak pernah menyentuh file secara langsung, melainkan meminta data ke modul Repo. Kemudian Repo meneruskan urusan baca tulis ke modul Penyimpanan, dan modul itulah yang berbicara dengan file catatan.json.

Diagram arsitektur aplikasi keuangan VB.NET: Form, Repo, Penyimpanan JSON, dan file data
Empat lapisan SC Keuangan: tampilan, logika, penyimpanan, dan file data.

Keuntungan pola ini cukup nyata. Misalnya, kelak Anda ingin pindah ke SQLite, maka cukup modul Penyimpanan dan sedikit bagian Repo yang berubah, sementara form tetap sama. Selain itu, logika seperti filter bulan dan saldo berada di satu tempat, sehingga mudah Anda uji dan baca ulang.

Langkah 1: Titik Masuk Program dan Budaya id-ID

Kita mulai dari Program.vb. Module ini mengatur format angka dan tanggal Indonesia sebelum form pertama muncul.

Friend Module Program

    <STAThread>
    Friend Sub Main()
        ' Format angka & tanggal Indonesia (15.000, Senin 07 November 2026)
        Dim budaya As New CultureInfo("id-ID")
        CultureInfo.DefaultThreadCurrentCulture = budaya
        CultureInfo.DefaultThreadCurrentUICulture = budaya
        Thread.CurrentThread.CurrentCulture = budaya
        Thread.CurrentThread.CurrentUICulture = budaya

        Application.SetHighDpiMode(HighDpiMode.SystemAware)
        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(False)

        ' Kesalahan tak terduga ditampilkan sebagai pesan, aplikasi tidak langsung tertutup
        AddHandler Application.ThreadException,
            Sub(s, e) Galat("Terjadi kesalahan: " & e.Exception.Message)

        Repo.MuatSemua()
        Application.Run(New FormUtama())
    End Sub

End Module

Budaya id-ID membuat titik menjadi pemisah ribuan dan nama bulan tampil dalam Bahasa Indonesia, misalnya “Oktober 2026”. Kemudian handler ThreadException menangkap error tak terduga lalu menampilkannya lewat Galat(), sehingga aplikasi tidak langsung tertutup. Terakhir, Repo.MuatSemua() membaca data dari file sebelum FormUtama tampil.

Langkah 2: Membuat Model Catatan

Satu transaksi keuangan cukup kita wakili dengan satu class. Buka Model.vb, lalu tulis class Catatan berikut.

Public Class Catatan
    Public Const MASUK As String = "Masuk"
    Public Const KELUAR As String = "Keluar"

    Public Property Id As Guid = Guid.NewGuid()
    Public Property Tanggal As Date = Date.Today
    Public Property Jenis As String = KELUAR
    Public Property Kategori As String = ""
    Public Property Keterangan As String = ""
    Public Property Jumlah As Decimal
    ' ...
End Class

Setiap catatan mendapat Id bertipe Guid yang unik, jadi kita tidak perlu memikirkan nomor urut. Selain itu, Jenis hanya berisi dua nilai konstanta, yaitu "Masuk" atau "Keluar". Perhatikan juga tipe Jumlah, yaitu Decimal. Tipe ini wajib untuk uang karena Double bisa menghasilkan selisih pembulatan kecil yang menyebalkan.

Properti Hitung IsMasuk dan Nilai

Berikutnya, tambahkan dua properti baca saja di dalam class yang sama.

    <JsonIgnore>
    Public ReadOnly Property IsMasuk As Boolean
        Get
            Return Jenis = MASUK
        End Get
    End Property

    ''' <summary>Jumlah bertanda: positif untuk pemasukan, negatif untuk pengeluaran.</summary>
    <JsonIgnore>
    Public ReadOnly Property Nilai As Decimal
        Get
            Return If(IsMasuk, Jumlah, -Jumlah)
        End Get
    End Property

Properti Nilai adalah trik kecil yang sangat membantu. Karena pengeluaran bernilai negatif, saldo cukup kita dapat dengan menjumlahkan semua Nilai. Atribut <JsonIgnore> membuat kedua properti ini tidak ikut masuk ke file JSON, sebab nilainya selalu bisa kita hitung ulang dari Jenis dan Jumlah. Agar atribut itu terbaca, jangan lupa menambahkan Imports System.Text.Json.Serialization di bagian atas file.

Struktur data aplikasi keuangan VB.NET dari objek Catatan ke file JSON dan CSV
Perjalanan satu catatan: objek di memori, isi catatan.json, lalu baris di file CSV.

Langkah 3: Penyimpanan JSON dengan System.Text.Json

Modul Penyimpanan bertugas membaca dan menulis file JSON di folder data. Karena memakai System.Text.Json bawaan .NET 8, kita tidak perlu memasang paket apa pun.

Public Module Penyimpanan

    Private ReadOnly Opsi As New JsonSerializerOptions With {.WriteIndented = True}

    Public ReadOnly Property FolderData As String
        Get
            Dim folder = Path.Combine(AppContext.BaseDirectory, "data")
            Directory.CreateDirectory(folder)
            Return folder
        End Get
    End Property

    Public Function AdaFile(namaFile As String) As Boolean
        Return File.Exists(Path.Combine(FolderData, namaFile))
    End Function
    ' ...

Opsi WriteIndented membuat file JSON rapi dan mudah Anda baca dengan Notepad. Kemudian properti FolderData menunjuk ke folder data di samping file .exe, sekaligus membuat folder tersebut bila belum ada. Fungsi AdaFile nanti berguna untuk mendeteksi apakah aplikasi baru pertama kali berjalan.

Membaca File dengan Muat(Of T)

    Public Function Muat(Of T As {Class, New})(namaFile As String) As T
        Dim lokasi = Path.Combine(FolderData, namaFile)
        If Not File.Exists(lokasi) Then Return New T()
        Try
            Dim hasil = JsonSerializer.Deserialize(Of T)(File.ReadAllText(lokasi), Opsi)
            Return If(hasil, New T())
        Catch ex As JsonException
            ' File rusak: simpan salinannya agar data lama tidak hilang begitu saja
            File.Copy(lokasi, lokasi & ".rusak-" & DateTime.Now.ToString("yyyyMMddHHmmss"), True)
            Galat("File " & namaFile & " rusak dan tidak bisa dibaca." & vbCrLf &
                  "Salinannya sudah disimpan di folder data, aplikasi memakai data kosong.")
            Return New T()
        End Try
    End Function

Fungsi ini generik, sehingga bisa memuat tipe apa saja selama tipe itu punya konstruktor kosong. Jika file belum ada, fungsi langsung mengembalikan objek baru yang kosong. Sebaliknya, jika isi file rusak, misalnya karena Anda mengeditnya manual dan lupa satu koma, fungsi ini menyalin file tersebut dengan akhiran .rusak- plus stempel waktu. Dengan demikian, data lama tidak hilang dan Anda masih bisa memperbaikinya nanti.

Menulis File dengan Aman

    Public Sub Simpan(Of T)(namaFile As String, data As T)
        Dim lokasi = Path.Combine(FolderData, namaFile)
        Dim sementara = lokasi & ".tmp"
        ' Tulis ke file sementara dulu, baru ganti file asli (aman bila listrik mati)
        File.WriteAllText(sementara, JsonSerializer.Serialize(data, Opsi))
        File.Move(sementara, lokasi, True)
    End Sub

Method Simpan tidak menimpa file asli secara langsung. Pertama, method ini menulis JSON ke catatan.json.tmp. Setelah penulisan selesai, barulah File.Move dengan parameter overwrite = True mengganti file asli. Akibatnya, bila listrik padam di tengah proses, file catatan.json yang lama tetap utuh.

Langkah 4: Repo sebagai Pusat Logika Data

Modul Repo menyimpan seluruh catatan di memori selama aplikasi berjalan. Selain itu, modul ini menyediakan daftar kategori bawaan.

Public Module Repo

    Private Const FILE_CATATAN As String = "catatan.json"

    Public ReadOnly KategoriMasuk As String() = {"Gaji", "Penjualan", "Bonus", "Lainnya"}
    Public ReadOnly KategoriKeluar As String() = {"Makan & Minum", "Transportasi", "Belanja", "Tagihan", "Pulsa & Internet", "Lainnya"}

    ' Semua catatan semua bulan, terisi sekali saat aplikasi mulai
    Public DaftarCatatan As New List(Of Catatan)()

    Public Sub MuatSemua()
        Dim pertamaKali = Not AdaFile(FILE_CATATAN)
        DaftarCatatan = Muat(Of List(Of Catatan))(FILE_CATATAN)
        If pertamaKali Then
            IsiDataContoh()
            SimpanCatatan()
        End If
    End Sub

    ' Tulis seluruh list ke catatan.json
    Public Sub SimpanCatatan()
        Simpan(FILE_CATATAN, DaftarCatatan)
    End Sub
    ' ...

Semua catatan dari semua bulan berada dalam satu List(Of Catatan) dan satu file. Untuk catatan pribadi atau UMKM kecil, pendekatan ini sudah lebih dari cukup, karena ribuan baris JSON tetap terbaca dalam sekejap. Kemudian MuatSemua mengecek apakah file belum ada. Jika benar begitu, Repo mengisi data contoh lalu langsung menyimpannya, sehingga pengguna baru tidak melihat layar kosong.

Filter Bulan dan Saldo Seluruhnya

    Public Function CariCatatan(id As Guid) As Catatan
        Return DaftarCatatan.FirstOrDefault(Function(c) c.Id = id)
    End Function

    ''' <summary>Catatan pada bulan tertentu (tanggal apa pun di bulan itu).</summary>
    Public Function CatatanBulan(bulan As Date) As List(Of Catatan)
        Return DaftarCatatan.Where(Function(c) c.Tanggal.Year = bulan.Year AndAlso c.Tanggal.Month = bulan.Month) _
                            .OrderByDescending(Function(c) c.Tanggal).ToList()
    End Function

    Public Function SaldoSeluruhnya() As Decimal
        Return DaftarCatatan.Sum(Function(c) c.Nilai)
    End Function

Ketiga fungsi ini memakai LINQ. Pertama, CariCatatan mencari satu catatan berdasarkan Id, dan fungsi ini penting saat proses ubah atau hapus. Kedua, CatatanBulan menyaring catatan yang tahun dan bulannya sama, lalu mengurutkannya dari tanggal terbaru. Terakhir, SaldoSeluruhnya cukup menjumlahkan properti Nilai yang sudah bertanda tadi.

Data Contoh untuk Pertama Kali

    Private Sub IsiDataContoh()
        ' Tanggal contoh di bulan berjalan, tidak melewati hari ini
        Dim hari = Function(d As Integer) New Date(Date.Today.Year, Date.Today.Month, Math.Min(d, Date.Today.Day))

        DaftarCatatan.AddRange({
            New Catatan With {.Tanggal = hari(1), .Jenis = Catatan.MASUK, .Kategori = "Gaji", .Keterangan = "Gaji dari CV Sharecode Mandiri", .Jumlah = 5000000},
            New Catatan With {.Tanggal = hari(5), .Jenis = Catatan.MASUK, .Kategori = "Penjualan", .Keterangan = "Jual source code di sharecode.id", .Jumlah = 750000},
            New Catatan With {.Tanggal = hari(2), .Jenis = Catatan.KELUAR, .Kategori = "Tagihan", .Keterangan = "Listrik & air", .Jumlah = 350000},
            ' ... lima catatan pengeluaran lain
        })
    End Sub

Lambda hari memastikan tanggal contoh tidak melewati hari ini. Misalnya aplikasi pertama kali berjalan tanggal 3, maka catatan “tanggal 7” otomatis bergeser ke tanggal 3. Akibatnya, data contoh selalu tampil di bulan berjalan tanpa tanggal di masa depan.

Langkah 5: Util untuk Format Rupiah dan Input Nominal

Modul Util berisi fungsi kecil yang form pakai berulang kali. Dua fungsi pertama mengurus format uang.

    Public ReadOnly Indo As New CultureInfo("id-ID")

    Public Function Rupiah(nilai As Decimal) As String
        Return "Rp " & nilai.ToString("N0", Indo)
    End Function

    ''' <summary>"Rp 15.000" / "15.000" / "15000" menjadi 15000.</summary>
    Public Function ParseAngka(teks As String) As Decimal
        Dim bersih = teks.Replace("Rp", "").Replace(" ", "").Trim()
        Dim hasil As Decimal
        If Decimal.TryParse(bersih, NumberStyles.Number, Indo, hasil) Then Return hasil
        Return 0D
    End Function

Format N0 dengan budaya Indonesia menghasilkan angka bertitik ribuan tanpa desimal, misalnya Rp 1.250.000. Sebaliknya, ParseAngka membuang teks “Rp” dan spasi, lalu mengubah teks kembali menjadi Decimal. Karena memakai TryParse, input yang aneh tidak memicu error, melainkan menghasilkan nol, lalu validasi menolaknya.

Modul yang sama juga punya Info, Peringatan, Galat, dan Tanya sebagai pembungkus MessageBox, serta GayaGrid yang menyeragamkan tampilan DataGridView, seperti header gelap, baris selang-seling, dan seleksi satu baris penuh.

TextBox Nominal dengan Titik Ribuan Otomatis

    Public Sub PasangInputRupiah(tb As TextBox)
        tb.MaxLength = 15
        AddHandler tb.KeyPress,
            Sub(s As Object, e As KeyPressEventArgs)
                If Not Char.IsControl(e.KeyChar) AndAlso Not Char.IsDigit(e.KeyChar) Then e.Handled = True
            End Sub

        ' Setiap teks berubah, susun ulang digit menjadi format ribuan
        AddHandler tb.TextChanged,
            Sub(s As Object, e As EventArgs)
                Dim digit = New String(tb.Text.Where(Function(c) Char.IsDigit(c)).ToArray())
                If digit.Length > 15 Then digit = digit.Substring(0, 15)
                Dim rapi = If(digit = "", "", Decimal.Parse(digit, Indo).ToString("N0", Indo))
                If tb.Text <> rapi Then
                    tb.Text = rapi
                    tb.SelectionStart = tb.Text.Length
                End If
            End Sub
    End Sub

Handler KeyPress menolak semua tombol selain angka dan tombol kontrol seperti Backspace. Kemudian handler TextChanged mengambil digitnya saja, memformat ulang dengan titik ribuan, lalu memindahkan kursor ke ujung teks. Pengecekan If tb.Text <> rapi penting agar event tidak memicu dirinya sendiri tanpa henti. Hasilnya, pengguna mengetik 65000 dan langsung melihat 65.000.

Langkah 6: Mendesain FormUtama

Buka FormUtama di Form Designer, lalu atur ukuran form sekitar 1100 × 680 dengan MinimumSize 1060 × 640. Aktifkan juga KeyPreview = True agar form bisa menangkap pintasan Ctrl+S. Tata letaknya terdiri dari tiga panel utama, yaitu header di atas, panel input di kiri, dan panel isi yang mengisi sisa ruang.

Nama kontrolTipeFungsi
pnlHeaderPanel (Dock Top)Judul aplikasi dan lblSaldoTotal di kanan.
pnlInputPanel (Dock Left)Wadah seluruh kontrol input catatan.
lblFormLabelJudul mode, berganti antara “Catatan Baru” dan “Ubah Catatan”.
dtpTanggalDateTimePickerTanggal transaksi.
rdoMasuk, rdoKeluarRadioButtonPilihan jenis pemasukan atau pengeluaran.
cboKategoriComboBox (DropDown)Pilih kategori atau ketik kategori baru.
txtKeteranganTextBoxKeterangan bebas.
txtJumlahTextBox (rata kanan)Nominal dengan titik ribuan otomatis.
btnSimpan, btnBaru, btnHapusButtonSimpan, kosongkan form, dan hapus catatan terpilih.
lblInfoLabelPesan status setelah simpan atau hapus.

Kontrol Ringkasan, Tabel, dan Grafik

Nama kontrolTipeFungsi
btnSebelum, btnBerikut, btnBulanIniButtonNavigasi bulan.
dtpBulanDateTimePickerFormat custom MMMM yyyy dengan ShowUpDown = True.
btnEksporButtonEkspor CSV bulan terpilih.
lblPemasukan, lblPengeluaran, lblSaldoLabel di tiga panel kartuAngka ringkasan bulanan.
lblKartu3LabelJudul kartu ketiga, misalnya “Selisih Oktober 2026”.
dgvCatatanDataGridViewKolom colTanggal, colKategori, colKeterangan, colJumlah.
pnlGrafikPanelGrafik (kontrol buatan)Grafik pengeluaran per kategori.

Kontrol PanelGrafik akan muncul di Toolbox setelah Anda melakukan build sekali. Karena itu, kerjakan dulu Langkah 8 bila Anda ingin menaruhnya lewat Designer.

Langkah 7: Logika FormUtama

Sekarang tekan F7 untuk pindah ke kode FormUtama.vb. Form ini menyimpan dua state, yaitu _idUbah untuk catatan yang sedang Anda ubah dan _tampil untuk catatan yang sedang tampil di tabel.

    ''' <summary>Id catatan yang sedang diubah. Guid.Empty = catatan baru.</summary>
    Private _idUbah As Guid = Guid.Empty

    ''' <summary>Catatan yang sedang tampil di tabel (urutan sama dengan baris).</summary>
    Private _tampil As New List(Of Catatan)()

    Private Sub FormUtama_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        GayaGrid(dgvCatatan)
        RataKanan(colJumlah)
        PasangInputRupiah(txtJumlah)

        dtpBulan.Value = AwalBulan(Date.Today)
        IsiKategori()
        Tampilkan()
        CatatanBaru()
    End Sub

    ' Helper: tanggal berapa pun menjadi tanggal 1 di bulan itu
    Private Shared Function AwalBulan(tgl As Date) As Date
        Return New Date(tgl.Year, tgl.Month, 1)
    End Function

Nilai Guid.Empty pada _idUbah berarti form sedang dalam mode catatan baru. Kemudian event Load memasang gaya grid, membuat kolom jumlah rata kanan, dan mengaktifkan input Rupiah. Setelah itu, form memilih bulan ini dan mengisi semua tampilan. Fungsi AwalBulan membantu membandingkan bulan tanpa repot dengan tanggal.

Kategori Dinamis dengan IsiKategori

    Private Sub IsiKategori()
        Dim bawaan = If(rdoMasuk.Checked, KategoriMasuk, KategoriKeluar)
        Dim buatan = DaftarCatatan.Where(Function(c) c.Jenis = JenisTerpilih) _
                                  .Select(Function(c) c.Kategori)
        Dim semua = bawaan.Concat(buatan.Where(Function(k) Not bawaan.Contains(k))).Distinct().ToArray()

        ' Pertahankan teks lama bila masih ada di daftar baru
        Dim teksLama = cboKategori.Text
        cboKategori.Items.Clear()
        cboKategori.Items.AddRange(semua)
        cboKategori.Text = If(semua.Contains(teksLama), teksLama, semua(0))
    End Sub

Inilah alasan kategori baru tidak perlu menu khusus. Prosedur ini menggabungkan kategori bawaan dengan semua kategori yang pernah Anda pakai pada jenis yang sama. Jadi, setelah Anda sekali mengetik “Bahan Baku”, kategori itu langsung muncul di pilihan pengeluaran berikutnya. Properti JenisTerpilih sendiri hanya mengembalikan Catatan.MASUK bila rdoMasuk tercentang, selain itu Catatan.KELUAR.

Menampilkan Tabel dan Kartu Ringkasan

    Private Sub Tampilkan()
        _tampil = CatatanBulan(BulanTerpilih)

        dgvCatatan.Rows.Clear()
        For Each c In _tampil
            Dim jumlah = If(c.IsMasuk, "+ ", "- ") & Rupiah(c.Jumlah)
            Dim i = dgvCatatan.Rows.Add(c.Tanggal.ToString("dd/MM/yyyy"), c.Kategori, c.Keterangan, jumlah)
            dgvCatatan.Rows(i).Cells(colJumlah.Index).Style.ForeColor = If(c.IsMasuk, Warna.Sukses, Warna.Bahaya)
        Next
        dgvCatatan.ClearSelection()

        ' Ringkasan bulan terpilih
        Dim masuk = _tampil.Where(Function(c) c.IsMasuk).Sum(Function(c) c.Jumlah)
        Dim keluar = _tampil.Where(Function(c) Not c.IsMasuk).Sum(Function(c) c.Jumlah)
        Dim selisih = masuk - keluar

        lblPemasukan.Text = Rupiah(masuk)
        lblPengeluaran.Text = Rupiah(keluar)
        lblSaldo.Text = If(selisih < 0, "- ", "") & Rupiah(Math.Abs(selisih))
        lblSaldo.ForeColor = If(selisih < 0, Warna.Bahaya, Warna.Primer)
        lblKartu3.Text = $"Selisih {BulanTerpilih:MMMM yyyy}"
        ' ...

Prosedur Tampilkan menjadi jantung layar utama. Pertama, prosedur ini mengambil catatan bulan terpilih ke _tampil. Urutan list ini sama persis dengan urutan baris tabel, sehingga indeks baris bisa langsung menunjuk ke objek Catatan. Kemudian setiap baris mendapat tanda plus hijau atau minus merah. Setelah itu, prosedur menghitung total pemasukan, pengeluaran, dan selisih. Jika selisih negatif, kartu ketiga berubah merah sebagai peringatan bahwa bulan ini besar pasak daripada tiang.

Menyiapkan Data Grafik dengan GroupBy

        Dim saldo = SaldoSeluruhnya()
        lblSaldoTotal.Text = "Saldo seluruhnya: " & If(saldo < 0, "- ", "") & Rupiah(Math.Abs(saldo))

        pnlGrafik.AturData(
            _tampil.Where(Function(c) Not c.IsMasuk) _
                   .GroupBy(Function(c) c.Kategori) _
                   .Select(Function(g) New KeyValuePair(Of String, Decimal)(g.Key, g.Sum(Function(c) c.Jumlah))))
    End Sub

Bagian akhir Tampilkan mengisi saldo seluruhnya di header, yaitu saldo dari semua bulan dan bukan hanya bulan terpilih. Selanjutnya, query LINQ mengambil pengeluaran saja, mengelompokkannya per kategori, lalu menjumlahkan tiap kelompok. Hasilnya berupa pasangan nama kategori dan total yang kita kirim ke pnlGrafik.AturData.

Menyimpan Catatan Baru atau Hasil Ubah

Alur simpan catatan di aplikasi keuangan VB.NET dari validasi sampai tampilan diperbarui
Lima tahap yang terjadi setiap kali Anda menekan tombol Simpan.
    Private Sub Simpan()
        Dim kategori = cboKategori.Text.Trim()
        Dim jumlah = ParseAngka(txtJumlah.Text)

        If kategori = "" Then Peringatan("Kategori wajib diisi.") : cboKategori.Focus() : Return
        If jumlah <= 0 Then Peringatan("Jumlah harus lebih dari 0.") : txtJumlah.Focus() : Return

        ' Cari catatan lama, atau buat baru bila tidak ada
        Dim c As Catatan = Nothing
        If _idUbah <> Guid.Empty Then c = CariCatatan(_idUbah)
        If c Is Nothing Then
            c = New Catatan()
            DaftarCatatan.Add(c)
        End If

        c.Tanggal = dtpTanggal.Value.Date
        c.Jenis = JenisTerpilih
        c.Kategori = kategori
        c.Keterangan = txtKeterangan.Text.Trim()
        c.Jumlah = jumlah
        SimpanCatatan()
        ' ...

Prosedur ini melayani dua mode sekaligus. Jika _idUbah berisi Id, prosedur mengambil catatan lama lewat CariCatatan. Sebaliknya, jika kosong, prosedur membuat Catatan baru dan menambahkannya ke list. Karena Catatan adalah class (tipe referensi), mengubah properti c berarti langsung mengubah objek di dalam DaftarCatatan. Terakhir, SimpanCatatan() menulis seluruh list ke JSON.

Pindah Otomatis ke Bulan Catatan

        lblInfo.Text = $"✔ Tersimpan: {c.Kategori} {Rupiah(c.Jumlah)}"
        ' Pindah ke bulan catatan tersebut agar langsung terlihat
        If AwalBulan(c.Tanggal) <> BulanTerpilih Then
            dtpBulan.Value = AwalBulan(c.Tanggal)   ' memicu Tampilkan()
        Else
            Tampilkan()
        End If
        IsiKategori()
        CatatanBaru()
    End Sub

Detail kecil ini membuat aplikasi terasa ramah. Misalnya Anda sedang melihat November tetapi mencatat pengeluaran tanggal 3 Oktober, maka tampilan otomatis pindah ke Oktober. Dengan begitu, Anda langsung melihat catatan yang baru tersimpan. Mengubah dtpBulan.Value memicu event ValueChanged, dan event itu menjalankan Tampilkan(). Setelah itu, IsiKategori menyegarkan daftar kategori, lalu CatatanBaru mengosongkan form.

Mengubah dan Menghapus Catatan

    Private Sub dgvCatatan_CellClick(sender As Object, e As DataGridViewCellEventArgs) Handles dgvCatatan.CellClick
        If e.RowIndex < 0 OrElse e.RowIndex >= _tampil.Count Then Return
        IsiForm(_tampil(e.RowIndex))
    End Sub

    ' Salin isi catatan ke form dan masuk mode ubah
    Private Sub IsiForm(c As Catatan)
        _idUbah = c.Id
        lblForm.Text = "Ubah Catatan"
        dtpTanggal.Value = c.Tanggal
        rdoMasuk.Checked = c.IsMasuk
        rdoKeluar.Checked = Not c.IsMasuk
        IsiKategori()
        cboKategori.Text = c.Kategori
        txtKeterangan.Text = c.Keterangan
        txtJumlah.Text = c.Jumlah.ToString("N0")
        btnHapus.Enabled = True
    End Sub

Saat Anda mengklik baris, event CellClick mengambil objek dari _tampil berdasarkan indeks baris. Pengecekan e.RowIndex < 0 mencegah error ketika yang Anda klik justru header kolom. Kemudian IsiForm menyalin data ke kontrol input, menyimpan Id ke _idUbah, dan mengaktifkan tombol Hapus.

Mode Ubah Catatan pada aplikasi keuangan VB.NET setelah baris tabel diklik
Mode “Ubah Catatan”: baris Obat flu terpilih, jumlah siap Anda ganti, dan tombol Hapus aktif.

Hapus dengan Konfirmasi

    Private Sub Hapus()
        Dim c = CariCatatan(_idUbah)
        If c Is Nothing Then Return
        If Not Tanya($"Hapus catatan {c.Kategori} {Rupiah(c.Jumlah)} tanggal {c.Tanggal:dd/MM/yyyy}?") Then Return

        DaftarCatatan.Remove(c)
        SimpanCatatan()
        Tampilkan()
        CatatanBaru()
        lblInfo.Text = "Catatan dihapus."
    End Sub

Penghapusan selalu melewati Tanya() yang menampilkan kategori, jumlah, dan tanggal catatan. Pesan yang spesifik seperti ini jauh lebih aman ketimbang sekadar “Yakin hapus?”, karena pengguna bisa memastikan baris yang benar sebelum menekan Yes. Setelah itu, prosedur menghapus objek dari list, menulis ulang file, lalu menyegarkan tampilan.

Navigasi Bulan dan Pintasan Keyboard

    Private Sub dtpBulan_ValueChanged(sender As Object, e As EventArgs) Handles dtpBulan.ValueChanged
        Tampilkan()
        If _idUbah = Guid.Empty Then CatatanBaru()
    End Sub

    Private Sub btnSebelum_Click(sender As Object, e As EventArgs) Handles btnSebelum.Click
        dtpBulan.Value = BulanTerpilih.AddMonths(-1)
    End Sub

    ' Ctrl+S dari mana saja (butuh KeyPreview = True)
    Private Sub FormUtama_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
        If e.Control AndAlso e.KeyCode = Keys.S Then
            e.SuppressKeyPress = True
            Simpan()
        End If
    End Sub

Semua tombol navigasi cukup mengubah dtpBulan.Value, lalu event ValueChanged mengurus sisanya. Tombol berikutnya memakai AddMonths(1), sedangkan Bulan Ini memakai AwalBulan(Date.Today). Selain Ctrl+S, kode asli juga menangani tombol Enter di txtJumlah dengan pola yang sama. Akibatnya, pencatatan cepat cukup dengan mengetik nominal lalu menekan Enter.

Langkah 8: Grafik Pengeluaran per Kategori dengan GDI+

Alih-alih memasang library chart, kita membuat kontrol sendiri yang mewarisi Panel. Cara ini membuat aplikasi tetap ringan dan sekaligus mengajarkan dasar menggambar di Windows Forms.

Public Class PanelGrafik
    Inherits Panel

    Private _data As New List(Of KeyValuePair(Of String, Decimal))()

    Public Property Judul As String = "Pengeluaran per kategori"

    Public Sub New()
        DoubleBuffered = True   ' cegah kedip saat digambar ulang
        ResizeRedraw = True     ' gambar ulang saat ukuran berubah
        BackColor = Color.White
    End Sub

    ' Terima data baru, urutkan dari terbesar, lalu minta gambar ulang
    Public Sub AturData(data As IEnumerable(Of KeyValuePair(Of String, Decimal)))
        _data = data.OrderByDescending(Function(x) x.Value).ToList()
        Invalidate()
    End Sub
    ' ...

Properti DoubleBuffered mencegah grafik berkedip, sedangkan ResizeRedraw membuat kontrol menggambar ulang setiap kali ukurannya berubah. Kemudian AturData mengurutkan kategori dari pengeluaran terbesar dan memanggil Invalidate(). Pemanggilan itu meminta Windows menjalankan OnPaint pada kesempatan berikutnya.

Menggambar Batang di OnPaint

    Protected Overrides Sub OnPaint(e As PaintEventArgs)
        MyBase.OnPaint(e)
        Dim g = e.Graphics
        ' ... siapkan font & kuas, gambar judul, cek data kosong

        Dim total = _data.Sum(Function(x) x.Value)
        Dim terbesar = _data.Max(Function(x) x.Value)
        Dim kanan = New StringFormat With {.Alignment = StringAlignment.Far}

        For Each item In _data
            Dim persen = If(total = 0, 0D, item.Value / total * 100D)
            g.DrawString(item.Key, fTeks, Brushes.Black, kiri, y)
            g.DrawString($"{Rupiah(item.Value)}  ({persen:0}%)", fAngka, kuasAbu,
                         New RectangleF(kiri, y, lebar, 20), kanan)
            y += 25

            ' Panjang batang relatif terhadap kategori terbesar
            Dim panjang = CSng(If(terbesar = 0, 0D, item.Value / terbesar) * lebar)
            g.FillRectangle(kuasJalur, kiri, y, lebar, 10)
            g.FillRectangle(kuasBatang, kiri, y, Math.Max(panjang, 2.0F), 10)
            y += 24

            If y > ClientSize.Height - 20 Then Exit For
        Next

Ada dua angka berbeda di sini. Persentase memakai total sebagai pembagi, sehingga semua persen berjumlah sekitar 100. Sebaliknya, panjang batang memakai terbesar sebagai pembagi, jadi kategori terbesar selalu memenuhi lebar panel dan perbandingan antarbatang tetap jelas. Batang abu-abu muda (kuasJalur) menjadi latar, lalu batang merah menimpanya. Kode asli membungkus font dan kuas di dalam blok Using agar resource GDI+ langsung terlepas.

Grafik batang pengeluaran per kategori di aplikasi keuangan VB.NET buatan GDI+
Hasil PanelGrafik untuk data Oktober: Bahan Baku memakan 39% pengeluaran.

Langkah 9: Ekspor CSV untuk Excel

Fitur ekspor membutuhkan helper kecil di Util untuk membungkus nilai yang mengandung karakter khusus.

    ''' <summary>Membungkus nilai CSV (pemisah titik koma, cocok untuk Excel Indonesia).</summary>
    Public Function Csv(nilai As Object) As String
        Dim teks = Convert.ToString(nilai, Indo)
        If teks.IndexOfAny({";"c, """"c, ControlChars.Cr, ControlChars.Lf}) >= 0 Then
            teks = """" & teks.Replace("""", """""") & """"
        End If
        Return teks
    End Function

Excel versi Indonesia memakai titik koma sebagai pemisah daftar, karena koma sudah berperan sebagai tanda desimal. Oleh karena itu, file CSV kita memakai titik koma. Jika sebuah keterangan kebetulan berisi titik koma, tanda kutip, atau baris baru, fungsi ini membungkusnya dengan tanda kutip ganda sesuai aturan CSV.

Prosedur EksporCsv

    Private Sub EksporCsv()
        If _tampil.Count = 0 Then
            Peringatan("Tidak ada catatan di bulan ini.")
            Return
        End If

        Using dlg As New SaveFileDialog()
            dlg.Filter = "File CSV (*.csv)|*.csv"
            dlg.FileName = $"keuangan_{BulanTerpilih:yyyy-MM}.csv"
            If dlg.ShowDialog(Me) <> DialogResult.OK Then Return

            Dim baris As New List(Of String) From {"Tanggal;Jenis;Kategori;Keterangan;Jumlah"}
            For Each c In _tampil.OrderBy(Function(x) x.Tanggal)
                baris.Add(String.Join(";", Csv(c.Tanggal.ToString("yyyy-MM-dd")), Csv(c.Jenis),
                                      Csv(c.Kategori), Csv(c.Keterangan), Csv(c.Nilai)))
            Next
            baris.Add("")
            baris.Add(";;;Total pemasukan;" & Csv(_tampil.Where(Function(c) c.IsMasuk).Sum(Function(c) c.Jumlah)))
            baris.Add(";;;Total pengeluaran;" & Csv(_tampil.Where(Function(c) Not c.IsMasuk).Sum(Function(c) c.Jumlah)))
            baris.Add(";;;Selisih;" & Csv(_tampil.Sum(Function(c) c.Nilai)))

            File.WriteAllLines(dlg.FileName, baris, New UTF8Encoding(True))
            Info("Data berhasil diekspor ke:" & vbCrLf & dlg.FileName)
        End Using
    End Sub

Nama file bawaan mengikuti bulan terpilih, misalnya keuangan_2026-10.csv. Kemudian prosedur mengurutkan catatan dari tanggal terlama agar enak Anda baca seperti buku kas. Kolom Jumlah memakai Nilai yang bertanda, sehingga Anda bisa langsung memakai rumus SUM di Excel. Terakhir, New UTF8Encoding(True) menulis BOM agar Excel tidak salah membaca karakter seperti “&”.

Contoh Isi File CSV

Berikut isi file hasil ekspor untuk data Oktober pada contoh kasus UMKM di bawah.

Tanggal;Jenis;Kategori;Keterangan;Jumlah
2026-10-01;Keluar;Sewa Tempat;Sewa kios Oktober (usaha);-1000000
2026-10-03;Keluar;Bahan Baku;Biji kopi & susu (usaha);-3200000
2026-10-05;Masuk;Penjualan;Omzet minggu 1 Kedai Kopi Sharecode;2100000
2026-10-10;Keluar;Tagihan;Listrik kios (usaha);-450000
2026-10-12;Masuk;Penjualan;Omzet minggu 2 Kedai Kopi Sharecode;2350000
2026-10-15;Keluar;Ambil Pribadi;Prive: uang belanja rumah;-1500000

Lima baris berikutnya beserta blok total di bagian bawah file:

2026-10-19;Masuk;Penjualan;Omzet minggu 3 Kedai Kopi Sharecode;1950000
2026-10-21;Keluar;Transportasi;Antar pesanan (usaha);-250000
2026-10-26;Masuk;Penjualan;Omzet minggu 4 Kedai Kopi Sharecode;2600000
2026-10-28;Keluar;Gaji Karyawan;Upah barista paruh waktu;-1800000

;;;Total pemasukan;9000000
;;;Total pengeluaran;8200000
;;;Selisih;800000

Baris kosong memisahkan data dari blok total. Selain itu, blok total menempati kolom keempat dan kelima, sehingga labelnya sejajar dengan kolom Keterangan saat Anda membukanya di Excel.

Contoh Kasus 1: UMKM Memisahkan Uang Usaha dan Pribadi

Bayangkan Bu Rina, pemilik Kedai Kopi Sharecode. Selama ini omzet, belanja bahan, dan uang belanja rumah keluar masuk dari satu laci. Akibatnya, setiap akhir bulan ia tidak tahu kedainya untung atau hanya terlihat ramai. Dengan SC Keuangan, Bu Rina membuat beberapa kategori baru cukup dengan mengetiknya, yaitu “Bahan Baku”, “Sewa Tempat”, “Gaji Karyawan”, dan satu kategori khusus “Ambil Pribadi” untuk uang yang ia bawa pulang.

Kuncinya ada di kategori “Ambil Pribadi” atau prive. Setiap kali mengambil uang kedai untuk kebutuhan rumah, ia mencatatnya sebagai pengeluaran dengan kategori ini. Dengan demikian, uang pribadi tetap tercatat tetapi terpisah jelas dari biaya usaha. Kolom keterangan juga ia beri penanda “(usaha)” agar mudah ia saring di Excel nanti.

Contoh kasus UMKM di aplikasi keuangan VB.NET: catatan Kedai Kopi Sharecode bulan Oktober
Catatan Oktober Kedai Kopi Sharecode: omzet Rp 9.000.000, pengeluaran Rp 8.200.000.

Membaca Hasil Bulan Oktober

Pada akhir Oktober, Bu Rina menekan tombol < sampai bulan Oktober. Kartu ringkasan menunjukkan pemasukan Rp 9.000.000, pengeluaran Rp 8.200.000, dan selisih Rp 800.000. Namun, angka itu belum menjawab apakah kedai untung. Karena pengeluaran juga memuat prive Rp 1.500.000, laba usaha sebenarnya adalah selisih plus prive, yaitu Rp 2.300.000.

Selanjutnya, grafik memperlihatkan bahwa Bahan Baku menjadi pengeluaran terbesar dengan porsi 39%, kemudian Gaji Karyawan 22% dan Ambil Pribadi 18%. Dari sini Bu Rina bisa mengambil keputusan konkret, misalnya mencari pemasok susu yang lebih murah atau membeli biji kopi dalam jumlah lebih besar. Terakhir, ia menekan Ekspor CSV dan mengirim file itu ke akuntan atau menyimpannya sebagai arsip.

Contoh Kasus 2: Mahasiswa dan Karyawan Mengatur Anggaran Bulanan

Kasus kedua datang dari Raka, karyawan baru di CV Sharecode Mandiri dengan gaji Rp 5.000.000. Ia ingin memakai aturan sederhana 50/30/20, yaitu 50% untuk kebutuhan, 30% untuk keinginan, dan 20% untuk tabungan. SC Keuangan memang belum punya fitur anggaran khusus. Meski begitu, fitur yang ada sudah cukup untuk menjalankan aturan tersebut secara konsisten.

Pertama, Raka mencatat gaji sebagai pemasukan kategori “Gaji” setiap tanggal gajian. Kemudian ia mencatat setiap pengeluaran harian, seperti makan siang, bensin, dan paket internet, segera setelah transaksi terjadi. Pintasan Enter di kolom jumlah membuat pencatatan cukup beberapa detik saja. Untuk tabungan, ia membuat kategori pengeluaran “Tabungan” setiap kali menyisihkan uang ke rekening lain.

Mengevaluasi Anggaran Setiap Akhir Bulan

Pada akhir bulan, Raka cukup melihat persentase di samping setiap batang grafik. Misalnya Makan & Minum ternyata 25% dari pengeluaran dan Belanja 37%, maka ia tahu pos mana yang perlu ia kurangi bulan depan. Selain itu, kartu selisih yang berubah merah menjadi alarm bahwa ia sudah memakai lebih dari gajinya.

Hal yang sama berlaku untuk mahasiswa yang menerima uang saku bulanan. Cukup ganti kategori “Gaji” dengan “Uang Saku” lalu catat pengeluaran kos, fotokopi, dan jajan. Karena kategori baru langsung tersimpan sebagai pilihan, aplikasi akan menyesuaikan diri dengan kebiasaan masing-masing pengguna tanpa perlu mengubah kode.

Menjalankan dan Membuat File Siap Pakai

Untuk menjalankan aplikasi, buka SCKeuangan.sln lalu tekan F5. Jika Anda tidak memakai Visual Studio, jalankan perintah berikut dari folder solution.

dotnet run --project SCKeuangan

dotnet publish SCKeuangan -c Release -r win-x64 --self-contained false -o publish

Perintah pertama langsung menjalankan aplikasi. Sementara itu, perintah kedua menghasilkan folder publish yang berisi SCKeuangan.exe. Komputer tujuan perlu .NET 8 Desktop Runtime. Jika Anda ingin aplikasi berjalan tanpa runtime terpasang, ganti --self-contained false menjadi true, meski ukuran folder hasilnya jadi jauh lebih besar.

Data tersimpan di data\catatan.json di samping file .exe. Untuk backup, cukup salin folder data ke flashdisk atau cloud. Sebaliknya, hapus file catatan.json bila Anda ingin mulai dari kosong, karena aplikasi akan membuat data contoh baru saat berjalan lagi.

Ide Pengembangan Lanjutan

  • Anggaran per kategori: tambahkan batas bulanan dan warnai batang grafik saat pengeluaran melewati batas.
  • Beberapa dompet: tambahkan properti Dompet (Kas Usaha, Rekening Pribadi, e-wallet) agar pemisahan uang lebih tegas.
  • SQLite: ganti modul Penyimpanan bila data sudah puluhan ribu baris atau perlu laporan tahunan yang kompleks.
  • Grafik tren: buat kontrol GDI+ kedua untuk membandingkan pemasukan dan pengeluaran 12 bulan terakhir.
  • Kata sandi: tambahkan form login sederhana dan enkripsi file JSON memakai System.Security.Cryptography.
  • Impor CSV: baca mutasi rekening bank agar pencatatan tidak perlu manual.

Kesalahan Umum

  • Membuka folder, bukan file .sln. Menu Open Folder membuat project tidak bisa Anda debug, jadi selalu buka SCKeuangan.sln.
  • Visual Studio 2019 tidak bisa membuka .NET 8. Perbarui ke Visual Studio 2022 versi 17.8 ke atas lewat Visual Studio Installer.
  • Lupa Option Strict. Tanpa OptionStrict On, konversi tipe implisit bisa lolos build lalu error saat aplikasi berjalan.
  • Memakai Double untuk uang. Gunakan Decimal agar tidak muncul selisih pembulatan seperti 0,1 + 0,2.
  • Budaya angka tertukar. Tanpa budaya id-ID, teks “15.000” bisa terbaca sebagai lima belas koma nol. Karena itu, kode ini selalu mengirim Indo ke TryParse dan ToString.
  • File data terkunci. Jangan membuka catatan.json di editor yang mengunci file saat aplikasi berjalan, dan hindari menaruh aplikasi di C:\Program Files yang butuh izin admin untuk menulis.
  • Kontrol PanelGrafik tidak muncul di Toolbox. Lakukan Build Solution (Ctrl+Shift+B) dulu, setelah itu tutup dan buka lagi tab form.
  • Kategori beda tulisan. “Bahan baku” dan “Bahan Baku” terhitung sebagai dua kategori, jadi pilih dari daftar ketimbang mengetik ulang.

Kesimpulan

Kita sudah membangun aplikasi keuangan VB.NET yang lengkap dari nol, mulai dari model Catatan dengan properti bertanda, penyimpanan JSON yang aman, Repo dengan filter bulan, sampai form utama dengan kartu ringkasan, grafik GDI+, dan ekspor CSV. Semua itu berjalan di atas .NET 8 tanpa database dan tanpa paket tambahan, sehingga mudah Anda pelajari maupun kembangkan.

Selain cocok sebagai bahan belajar, aplikasi ini juga langsung berguna untuk mencatat keuangan pribadi atau memisahkan uang usaha seperti pada contoh Kedai Kopi Sharecode. Jadi, silakan download source code SC Keuangan VB.NET gratis di sharecode.id, buka di Visual Studio 2022, lalu coba tambahkan salah satu ide pengembangan di atas. Lisensinya MIT, jadi Anda bebas memakainya untuk tugas kuliah, skripsi, maupun usaha.

Baca Juga

Referensi: Windows Forms untuk .NET (Microsoft Learn), Serialisasi dan deserialisasi JSON dengan System.Text.Json, Kontrol DataGridView (Windows Forms), Menggunakan objek Graphics dan GDI+ di Windows Forms.

Ali Akbar

Software Developer yang fokus mengembangkan aplikasi berbasis Web dan Desktop. Senang mempelajari teknologi baru terutama di bidang web design dan web development.

View all posts by Ali Akbar →

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *