Tại sao một developer cần học kế toán?

Hầu hết developer khi nghe “kế toán” đều nghĩ đây là công việc của phòng tài vụ. Nhưng trong Core Banking, kế toán kép chính là business logic cốt lõi nhất mà code của bạn phải thực thi. Nếu code của bạn sai, sổ cái mất cân bằng, ngân hàng không thể báo cáo lên Ngân hàng Nhà nước và hậu quả pháp lý là vô cùng nghiêm trọng.

Nguyên lý Kế toán Kép (Double-Entry Bookkeeping)

Được phát minh từ thế kỷ 15 bởi nhà toán học người Ý Luca Pacioli, nguyên lý này chỉ có một quy tắc duy nhất:

Mọi giao dịch tài chính đều phải được ghi chép vào ít nhất hai tài khoản, một bên Nợ (Debit) và một bên Có (Credit), với tổng giá trị hai bên phải bằng nhau.

Ví dụ thực tế: Khách hàng A chuyển 1.000.000 VNĐ cho khách hàng B

Trong tư duy thông thường, developer hay nghĩ đơn giản:

account_A.balance -= 1_000_000
account_B.balance += 1_000_000

Đây là sai về mặt kế toán. Cách đúng là ghi vào bảng General Ledger (GL):

IDTài khoản (Account)Loại bút toánSố tiền
TX1Tài khoản ANợ (Debit)1.000.000
TX1Tài khoản BCó (Credit)1.000.000

Tổng Nợ = Tổng Có = 1.000.000 → Sổ cái cân bằng ✅

Bảng General Ledger (GL) — Trái tim của Core Banking

Toàn bộ hệ thống Core Banking thực ra chỉ xoay quanh việc ghi dữ liệu vào bảng GL một cách chính xác. Đây là thiết kế cơ bản nhất của bảng GL:

CREATE TABLE ledger_entries (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    transaction_id  UUID        NOT NULL,  -- Nhóm các bút toán cùng giao dịch
    account_id      UUID        NOT NULL,  -- Tài khoản nào bị ảnh hưởng
    entry_type      VARCHAR(6)  NOT NULL,  -- 'DEBIT' hoặc 'CREDIT'
    amount          BIGINT      NOT NULL,  -- Lưu theo đơn vị nhỏ nhất (VD: đồng, cents)
    currency        CHAR(3)     NOT NULL,  -- 'VND', 'USD', 'JPY'
    balance_after   BIGINT      NOT NULL,  -- Số dư sau bút toán này
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    description     TEXT,
    
    CONSTRAINT chk_amount_positive CHECK (amount > 0),
    CONSTRAINT chk_entry_type CHECK (entry_type IN ('DEBIT', 'CREDIT'))
);

Lưu ý quan trọng: Luôn lưu tiền theo đơn vị nguyên (integer) — đồng, cents, satoshi. Không bao giờ dùng FLOAT hay DOUBLE để lưu tiền vì lỗi làm tròn số thực (floating-point precision error) sẽ làm sổ cái mất cân bằng sau hàng nghìn giao dịch.

Kiểm tra sức khoẻ sổ cái: The Balance Invariant

Đây là câu query bạn phải có khả năng chạy bất kỳ lúc nào để xác nhận sổ cái không bị hỏng:

-- Tổng tất cả bút toán Nợ PHẢI LUÔN bằng tổng tất cả bút toán Có
SELECT
    SUM(CASE WHEN entry_type = 'DEBIT'  THEN amount ELSE 0 END) AS total_debits,
    SUM(CASE WHEN entry_type = 'CREDIT' THEN amount ELSE 0 END) AS total_credits,
    SUM(CASE WHEN entry_type = 'DEBIT'  THEN amount ELSE 0 END) -
    SUM(CASE WHEN entry_type = 'CREDIT' THEN amount ELSE 0 END) AS imbalance
FROM ledger_entries;

-- Kết quả mong đợi: imbalance = 0

Nếu imbalance ≠ 0, có nghĩa là code của bạn đã ghi thiếu một bút toán ở đâu đó — đây là lỗi nghiêm trọng nhất trong Core Banking.

Cấu trúc tài khoản trong ngân hàng

Không phải tài khoản nào cũng là tài khoản của khách hàng. Trong ngân hàng, có nhiều loại tài khoản nội bộ:

Loại tài khoảnÝ nghĩaVí dụ
Asset (Tài sản)Tiền ngân hàng đang nắm giữTiền mặt tại quỹ, Cho vay KH
Liability (Nợ phải trả)Tiền ngân hàng nợ khách hàngSố dư tài khoản của KH
Income (Thu nhập)Doanh thu của ngân hàngPhí giao dịch, lãi vay thu
Expense (Chi phí)Chi phí của ngân hàngLãi tiết kiệm phải trả
Equity (Vốn chủ sở hữu)Vốn của cổ đôngVốn điều lệ

Khi khách hàng gửi 10 triệu vào tài khoản tiết kiệm, hệ thống phải ghi:

  • Nợ Tài khoản Tiền Mặt (Asset tăng lên)
  • Tài khoản Tiết Kiệm Khách Hàng (Liability tăng lên — ngân hàng đang nợ KH)

Bài học cốt lõi

  1. Không bao giờ chỉ cập nhật số dư trực tiếp (UPDATE accounts SET balance = balance - X). Luôn ghi bút toán vào ledger, rồi tính số dư từ ledger.
  2. Mỗi giao dịch là một đơn vị nguyên tử — tất cả các bút toán phải cùng thành công hoặc cùng thất bại (Database Transaction).
  3. Ledger là immutable — không bao giờ UPDATE hay DELETE một bút toán đã ghi. Để sửa lỗi, bạn ghi thêm bút toán đảo chiều (reversal entry).

Tiếp theo, chúng ta sẽ áp dụng tư duy kế toán kép vào các nghiệp vụ cụ thể nhất của ngân hàng. Đọc tiếp Phần 2 — Nghiệp vụ Ngân hàng Lõi: CIF, CASA & Lending.