Sau khi hoàn thành bài học này, người học có thể:
Nắm vững bản chất Bảng Cân đối Kế toán (Balance Sheet): Hiểu rõ phương trình kế toán cơ bản ($\text{Tài sản} = \text{Nợ phải trả} + \text{Vốn chủ sở hữu}$) và cách phản ánh trạng thái tài chính tại một thời điểm cụ thể (as_of_date).
Hiểu cơ chế tài khoản lưỡng tính và số dư ròng: Xử lý chính xác các tài khoản có thể có số dư hai bên (như TK 131 - Phải thu khách hàng, TK 331 - Phải trả người bán) để phân loại đúng vào Tài sản hoặc Nợ phải trả.
Xây dựng thuật toán SQL trích xuất Số dư Cuối kỳ dồn tích: Viết các truy vấn tính toán số dư lũy kế từ ngày đầu khởi tạo hệ thống đến thời điểm lập báo cáo.
Đóng gói Python API Báo cáo Cân đối Kế toán: Lập trình hàm Python tự động kiểm tra cân đối tổng thể ($\text{Tổng Tài sản} - \text{Tổng Nguồn vốn} = 0$) và xuất kết quả ra Pandas DataFrame.
Bảng Cân đối Kế toán phản ánh toàn bộ Tài sản và Nguồn vốn của doanh nghiệp tại thời điểm cuối ngày của ngày báo cáo (as_of_date).
┌─────────────────────────────────────────────────────────────────────────┐
│ TỔNG TÀI SẢN (ASSETS) │
│ Tài sản Ngắn hạn (Loại 1) + Tài sản Dài hạn (Loại 2) │
├─────────────────────────────────────────────────────────────────────────┤
│ || │
│ BẰNG (EQUAL TO) │
│ || │
├─────────────────────────────────────────────────────────────────────────┤
│ TỔNG NGUỒN VỐN (EQUITY & LIABILITIES) │
│ Nợ phải trả (Loại 3) + Vốn chủ sở hữu (Loại 4) │
└─────────────────────────────────────────────────────────────────────────┘
Số dư Dồn tích (Cumulative Balance):
Khác với Báo cáo P&L chỉ lấy trong khoảng thời gian, Bảng Cân đối Kế toán lấy tổng lũy kế toàn bộ bút toán từ trước tới thời điểm as_of_date:
Xử lý Bản chất Tài khoản:
Tài khoản Tài sản (Loại 1, 2): Số dư chuẩn là Nợ ($\text{Debit} > \text{Credit}$).
Ngoại lệ (Tài khoản điều chỉnh giảm): TK 214 (Hao mòn tài sản cố định), TK 229 (Dự phòng tổn thất tài sản) có bản chất số dư Có, ghi giá trị ÂM bên cột Tài sản.
Tài khoản Nguồn vốn (Loại 3, 4): Số dư chuẩn là Có ($\text{Credit} > \text{Debit}$).
Tài khoản Lưỡng tính (TK 131, 331):
TK 131 dư Nợ $\rightarrow$ Phải thu khách hàng (Tài sản).
TK 131 dư Có $\rightarrow$ Người mua trả tiền trước (Nợ phải trả).
TK 331 dư Có $\rightarrow$ Phải trả người bán (Nợ phải trả).
TK 331 dư Nợ $\rightarrow$ Trả trước cho người bán (Tài sản).
Dưới đây là mã nguồn Python chiết xuất dữ liệu số dư lũy kế từ SQL và lập Bảng Cân đối Kế toán.
import sqlite3
import pandas as pd
def get_balance_sheet(as_of_date: str, db_name: str = "accounting_mini.db") -> dict:
"""
Trích xuất Bảng Cân đối Kế toán tại thời điểm cuối ngày as_of_date.
"""
conn = sqlite3.connect(db_name)
# 1. Truy vấn tổng lũy kế Nợ/Có đến thời điểm as_of_date cho các TK Bảng Cân đối (Loại 1, 2, 3, 4)
query = """
SELECT
a.account_code,
a.account_name,
a.normal_balance,
COALESCE(SUM(j.debit_amount), 0.0) AS cum_debit,
COALESCE(SUM(j.credit_amount), 0.0) AS cum_credit
FROM accounts a
LEFT JOIN journal_entries j ON a.account_code = j.account_code
LEFT JOIN transactions t ON j.transaction_id = t.transaction_id AND t.entry_date <= ?
WHERE a.account_code LIKE '1%' OR a.account_code LIKE '2%'
OR a.account_code LIKE '3%' OR a.account_code LIKE '4%'
GROUP BY a.account_code, a.account_name, a.normal_balance
ORDER BY a.account_code ASC
"""
df_raw = pd.read_sql_query(query, conn, params=[as_of_date])
conn.close()
# Dictionary lưu trữ số dư ròng theo tài khoản
# Quy ước: Dương = Nợ, Âm = Có
balances = {}
for _, row in df_raw.iterrows():
code = str(row["account_code"])
net = row["cum_debit"] - row["cum_credit"]
balances[code] = net
# Hàm lấy số dư tài khoản (trả về 0 nếu chưa có)
def b(code: str) -> float:
return balances.get(code, 0.0)
# -------------------------------------------------------------------------
# 2. TÍNH TOÁN CÁC CHỈ TIÊU TÀI SẢN (ASSETS)
# -------------------------------------------------------------------------
# A. TÀI SẢN NGẮN HẠN
cash_and_equiv = b("111") + b("112") # Tiền và tương đương tiền
# Xử lý TK 131 Lưỡng tính: Chỉ lấy phần dư Nợ vào Tài sản
receivables_131 = b("131") if b("131") > 0 else 0.0
prepaid_331 = abs(b("331")) if b("331") < 0 else 0.0 # Trả trước cho người bán (nếu dư Nợ)
trade_receivables = receivables_131 + prepaid_331 + b("138")
inventory = b("152") + b("155") + b("156") # Hàng tồn kho
other_short_assets = b("141") + b("133") # Tài sản ngắn hạn khác
total_short_term_assets = cash_and_equiv + trade_receivables + inventory + other_short_assets
# B. TÀI SẢN DÀI HẠN
fixed_assets_cost = b("211") # Nguyên giá TSCĐ
accumulated_depreciation = b("214") # Giá trị hao mòn lũy kế (thường mang dấu âm/Có)
fixed_assets_net = fixed_assets_cost + accumulated_depreciation # Giá trị còn lại
total_long_term_assets = fixed_assets_net + b("241") + b("228")
# TỔNG TÀI SẢN
total_assets = total_short_term_assets + total_long_term_assets
# -------------------------------------------------------------------------
# 3. TÍNH TOÁN CÁC CHỈ TIÊU NGUỒN VỐN (EQUITY & LIABILITIES)
# -------------------------------------------------------------------------
# A. NỢ PHẢI TRẢ
payables_331 = abs(b("331")) if b("331") > 0 else 0.0 # Phải trả người bán (dư Có)
advance_131 = abs(b("131")) if b("131") < 0 else 0.0 # Người mua trả tiền trước (dư Có)
trade_payables = payables_331 + advance_131 + abs(b("334")) + abs(b("338"))
tax_payables = abs(b("3331")) + abs(b("3334")) # Thuế và các khoản phải nộp nhà nước
short_term_loans = abs(b("341")) # Vay ngắn hạn
total_liabilities = trade_payables + tax_payables + short_term_loans
# B. VỐN CHỦ SỞ HỮU
owner_capital = abs(b("411")) # Vốn góp của chủ sở hữu
retained_earnings = abs(b("4211")) + abs(b("4212")) # Lợi nhuận sau thuế chưa phân phối
if b("4212") < 0: # Trường hợp lỗ
retained_earnings = abs(b("4211")) - abs(b("4212"))
total_equity = owner_capital + retained_earnings
# TỔNG NGUỒN VỐN
total_resources = total_liabilities + total_equity
# -------------------------------------------------------------------------
# 4. ĐÓNG GÓI BẢNG BÁO CÁO
# -------------------------------------------------------------------------
bs_structure = [
# TÀI SẢN
{"Mã số": "100", "Chỉ tiêu": "A. TÀI SẢN NGẮN HẠN", "Số tiền": total_short_term_assets},
{"Mã số": "110", "Chỉ tiêu": " I. Tiền và các khoản tương đương tiền", "Số tiền": cash_and_equiv},
{"Mã số": "130", "Chỉ tiêu": " II. Các khoản phải thu ngắn hạn", "Số tiền": trade_receivables},
{"Mã số": "140", "Chỉ tiêu": " III. Hàng tồn kho", "Số tiền": inventory},
{"Mã số": "150", "Chỉ tiêu": " IV. Tài sản ngắn hạn khác", "Số tiền": other_short_assets},
{"Mã số": "200", "Chỉ tiêu": "B. TÀI SẢN DÀI HẠN", "Số tiền": total_long_term_assets},
{"Mã số": "220", "Chỉ tiêu": " I. Tài sản cố định (Nguyên giá - Hao mòn)", "Số tiền": fixed_assets_net},
{"Mã số": "270", "Chỉ tiêu": "TỔNG CỘNG TÀI SẢN (270 = 100 + 200)", "Số tiền": total_assets},
# NGUỒN VỐN
{"Mã số": "300", "Chỉ tiêu": "C. NỢ PHẢI TRẢ", "Số tiền": total_liabilities},
{"Mã số": "310", "Chỉ tiêu": " I. Nợ ngắn hạn (Phải trả người bán, Thuế...)", "Số tiền": total_liabilities},
{"Mã số": "400", "Chỉ tiêu": "D. VỐN CHỦ SỞ HỮU", "Số tiền": total_equity},
{"Mã số": "411", "Chỉ tiêu": " I. Vốn góp của chủ sở hữu", "Số tiền": owner_capital},
{"Mã số": "421", "Chỉ tiêu": " II. Lợi nhuận sau thuế chưa phân phối", "Số tiền": retained_earnings},
{"Mã số": "440", "Chỉ tiêu": "TỔNG CỘNG NGUỒN VỐN (440 = 300 + 400)", "Số tiền": total_resources},
]
df_bs = pd.DataFrame(bs_structure)
diff = total_assets - total_resources
is_balanced = abs(diff) < 1e-5
return {
"df": df_bs,
"as_of_date": as_of_date,
"total_assets": total_assets,
"total_resources": total_resources,
"is_balanced": is_balanced,
"difference": diff
}
Dưới đây là kịch bản chạy thử nghiệm xuất Bảng Cân đối Kế toán tại ngày 30/09/2026 (sau khi đã thực thi bút toán kết chuyển ở Bài 3.1).
if __name__ == "__main__":
# Trích xuất Bảng Cân đối Kế toán tại ngày 30/09/2026
bs_result = get_balance_sheet("2026-09-30")
df_bs = bs_result["df"]
pd.set_option('display.max_columns', None)
pd.set_option('display.width', 1000)
pd.set_option('display.float_format', lambda x: '{:,.0f}'.format(x))
print("==========================================================================")
print(" BẢNG CÂN ĐỐI KẾ TOÁN (BALANCE SHEET) ")
print(f" Tại ngày: {bs_result['as_of_date']} ")
print("==========================================================================")
print(df_bs.to_string(index=False))
print("--------------------------------------------------------------------------")
print(f" TỔNG TÀI SẢN : {bs_result['total_assets']:,.0f} VNĐ")
print(f" TỔNG NGUỒN VỐN: {bs_result['total_resources']:,.0f} VNĐ")
print(f" CHÊNH LỆCH : {bs_result['difference']:,.0f} VNĐ")
print("--------------------------------------------------------------------------")
if bs_result["is_balanced"]:
print("[XÁC NHẬN] BẢNG CÂN ĐỐI KẾ TOÁN HOÀN TOÀN CÂN BẰNG! (Phương trình Kế toán đúng)")
else:
print("[CẢNH BÁO LỖI] Bảng Cân đối Kế toán bị MẤT CÂN BẰNG. Kiểm tra lại bút toán!")
print("==========================================================================")
==========================================================================
BẢNG CÂN ĐỐI KẾ TOÁN (BALANCE SHEET)
Tại ngày: 2026-09-30
==========================================================================
Mã số Chỉ tiêu Số tiền
100 A. TÀI SẢN NGẮN HẠN 105,000,000
110 I. Tiền và các khoản tương đương tiền 105,000,000
130 II. Các khoản phải thu ngắn hạn 0
140 III. Hàng tồn kho 0
150 IV. Tài sản ngắn hạn khác 0
200 B. TÀI SẢN DÀI HẠN 0
220 I. Tài sản cố định (Nguyên giá - Hao mòn) 0
270 TỔNG CỘNG TÀI SẢN (270 = 100 + 200) 105,000,000
300 C. NỢ PHẢI TRẢ 1,000,000
310 I. Nợ ngắn hạn (Phải trả người bán, Thuế...) 1,000,000
400 D. VỐN CHỦ SỞ HỮU 104,000,000
411 I. Vốn góp của chủ sở hữu 100,000,000
421 II. Lợi nhuận sau thuế chưa phân phối 4,000,000
440 TỔNG CỘNG NGUỒN VỐN (440 = 300 + 400) 105,000,000
--------------------------------------------------------------------------
TỔNG TÀI SẢN : 105,000,000 VNĐ
TỔNG NGUỒN VỐN: 105,000,000 VNĐ
CHÊNH LỆCH : 0 VNĐ
--------------------------------------------------------------------------
[XÁC NHẬN] BẢNG CÂN ĐỐI KẾ TOÁN HOÀN TOÀN CÂN BẰNG! (Phương trình Kế toán đúng)
==========================================================================
Nếu doanh nghiệp chưa chạy bút toán kết chuyển xác định kết quả kinh doanh cuối kỳ (Bài 3.1), Bảng Cân đối Kế toán sẽ bị rơi vào trạng thái nào?
A. Bảng Cân đối Kế toán vẫn tự động cân bằng vì SQL tự tính Lợi nhuận.
B. Bảng Cân đối Kế toán bị mất cân bằng đúng bằng giá trị Lợi nhuận sau thuế phát sinh trong kỳ, do Doanh thu và Chi phí chưa được chuyển về TK 421.
C. Tổng Tài sản sẽ bị giảm đi 20%.
Hãy bổ sung đoạn mã xử lý trường hợp doanh nghiệp có trích lập Hao mòn Tài sản cố định (TK 2141 dư Có) để hiển thị riêng dòng Nguyên giá (211) và Hao mòn lũy kế (2141) dạng số âm trong nhóm Chỉ tiêu Tài sản cố định.
Bài tập 1:
Đáp án đúng là B. Nếu chưa kết chuyển, Doanh thu và Chi phí nằm ở TK Loại 5–8 (không thuộc Bảng Cân đối Kế toán) và chưa chảy về TK 421 (Vốn chủ sở hữu), dẫn đến $\text{Tài sản} \ne \text{Nguồn vốn}$.
Bài tập 2:
# Trong hàm get_balance_sheet:
cost = b("211")
accum_dep = b("2141") # Giá trị mang dấu âm (do dư Có)
fixed_asset_rows = [
{"Mã số": "221", "Chỉ tiêu": " - Nguyên giá", "Số tiền": cost},
{"Mã số": "222", "Chỉ tiêu": " - Giá trị hao mòn lũy kế", "Số tiền": accum_dep},
{"Mã số": "220", "Chỉ tiêu": " I. Giá trị còn lại TSCĐ", "Số tiền": cost + accum_dep}
]