Model Transaction sudah siap. Sekarang kita bangun Finance sebagai wadah koleksi transaksi dengan kemampuan menambah dan menghapus. Sama seperti Inventory di proyek sebelumnya, Finance memakai array statis dengan next_id untuk menjamin id unik. Yang berbeda: tiap elemen punya tipe (income/expense) yang akan kita pakai di perhitungan saldo di bagian 4.
Konsep #
Operasi dasar koleksi Finance:
finance_add— tambah transaksi baru. Fungsi menerima field transaksi (bukanTransaction*jadi), memanggiltransaction_createpada slot kosong, dan mengembalikan id baru.finance_delete— hapus transaksi berdasarkan id. Pakaimemmoveuntuk menggeser elemen setelahnya, sama seperti pola diInventory.finance_print_all— cetak seluruh transaksi dengan format tabel.
Mekanisme kerja: array items dialokasikan di stack dengan ukuran FINANCE_CAPACITY = 512. Id increment dari next_id yang dimulai dari 1. Bila kapasitas penuh, finance_add mengembalikan -1 dan tidak mengubah state.
Implementasi #
Header finance.h:
/* finance.h - Koleksi transaksi dengan operasi dasar */
#ifndef FINANCE_H
#define FINANCE_H
#include "transaction.h"
#define FINANCE_CAPACITY 512
typedef struct {
Transaction items[FINANCE_CAPACITY];
int count;
int next_id;
} Finance;
void finance_init(Finance *f);
int finance_add(Finance *f, TxnType type, double amount,
const char *description, const char *category,
const char *date);
int finance_delete(Finance *f, int id);
void finance_print_all(const Finance *f);
#endif /* FINANCE_H */Implementasi:
/* finance.c - Implementasi operasi dasar Finance */
#include "finance.h"
#include <stdio.h>
#include <string.h>
void finance_init(Finance *f) {
if (f == NULL) {
return;
}
f->count = 0;
f->next_id = 1;
}
int finance_add(Finance *f, TxnType type, double amount,
const char *description, const char *category,
const char *date) {
if (f == NULL || description == NULL ||
category == NULL || date == NULL) {
return -1;
}
if (f->count >= FINANCE_CAPACITY) {
return -1;
}
Transaction *slot = &f->items[f->count];
int id = f->next_id;
if (transaction_create(slot, id, type, amount,
description, category, date) != 0) {
return -1;
}
f->next_id++;
f->count++;
return id;
}
int finance_delete(Finance *f, int id) {
if (f == NULL) {
return -1;
}
for (int i = 0; i < f->count; i++) {
if (f->items[i].id == id) {
memmove(&f->items[i], &f->items[i + 1],
(size_t)(f->count - i - 1) * sizeof(Transaction));
f->count--;
return 0;
}
}
return -1;
}
void finance_print_all(const Finance *f) {
if (f == NULL) {
return;
}
printf("Total transaksi: %d/%d\n", f->count, FINANCE_CAPACITY);
for (int i = 0; i < f->count; i++) {
transaction_print(&f->items[i]);
}
}Verifikasi operasi dasar:
/* demo_finance.c - Uji coba add/delete */
#include <stdio.h>
#include "finance.h"
int main(void) {
Finance f;
finance_init(&f);
int id1 = finance_add(&f, TXN_INCOME, 5000000.0,
"Gaji", "Gaji", "2024-03-01");
int id2 = finance_add(&f, TXN_EXPENSE, 50000.0,
"Bensin", "Transport", "2024-03-02");
int id3 = finance_add(&f, TXN_EXPENSE, 30000.0,
"Makan", "Makan", "2024-03-02");
printf("Tambah: id1=%d id2=%d id3=%d count=%d\n",
id1, id2, id3, f.count);
finance_print_all(&f);
/* Hapus id2 dan verifikasi shift. */
int rc = finance_delete(&f, id2);
printf("\nSetelah delete id=%d: rc=%d count=%d\n", id2, rc, f.count);
finance_print_all(&f);
/* Delete id yang tidak ada. */
rc = finance_delete(&f, 999);
printf("\nDelete id=999: rc=%d (harus -1)\n", rc);
return 0;
}Output yang Dihasilkan:
Tambah: id1=1 id2=2 id3=3 count=3
Total transaksi: 3/512
[0001] 2024-03-01 MASUK Gaji Rp 5000000.00 Gaji
[0002] 2024-03-02 KELUAR Transport Rp -50000.00 Bensin
[0003] 2024-03-02 KELUAR Makan Rp -30000.00 Makan
Setelah delete id=2: rc=0 count=2
Total transaksi: 2/512
[0001] 2024-03-01 MASUK Gaji Rp 5000000.00 Gaji
[0003] 2024-03-02 KELUAR Makan Rp -30000.00 Makan
Delete id=999: rc=-1 (harus -1)Mari kita telaah baris-baris kode di atas:
- Baris 13-15 (
finance_init): Sama sepertiinventory_init. Resetcount = 0dannext_id = 1. Tidak perlu zero-initialize array items karena count = 0 sudah menandakan array kosong. - Baris 17-34 (
finance_add): Pattern yang sama denganinventory_add. Id didapat darinext_idsebelum diincrement. Bilatransaction_creategagal, fungsi kembali -1 tanpa mengubahcountataunext_id. Id yang gagal tidak di-skip, sehingga id urut tetap konsisten (no gap karena validation failure). - Baris 36-47 (
finance_delete): Linear search untuk menemukan id, lalumemmoveuntuk menggeser elemen setelahnya satu posisi kiri.memmovedipakai alih-alihmemcpykarena source dan destination bisa overlap. Ukuran yang dipindahkan adalah(f->count - i - 1) * sizeof(Transaction)yang menjadi 0 bilaiadalah indeks terakhir (no-op). - Baris 49-55 (
finance_print_all): Cetak header dengan kapasitas total (count/capacity) lalu iterasi semua transaksi. Format%d/%dmemberi tahu pengguna berapa banyak slot yang sudah dipakai. - Baris 70-71 (verifikasi shift): Setelah delete id=2, transaksi id=3 harus tetap ada di posisi index 1 (bukan 2).
memmovemenggeser dengan benar.
Kode Lengkap #
Kode lengkap untuk finance.h dan finance.c ada di direktori proyek. Filter dan summary akan ditambahkan di bagian 3 dan 4.
Pengujian #
Test edge case yang harus dipastikan:
finance_adddengan parameter NULL mengembalikan -1.finance_adddengan amount negatif mengembalikan -1 (daritransaction_create).finance_adddengan tanggal invalid mengembalikan -1.finance_deletepada id yang sudah dihapus mengembalikan -1.finance_deletepada Finance kosong mengembalikan -1.- Setelah
finance_delete,countberkurang satu dan elemen setelahnya digeser dengan benar.
gcc -std=c11 -Wall -Wextra -o /tmp/demo_finance demo_finance.c \
finance.c transaction.c -lm
/tmp/demo_finance