Sau khi hoàn thành bài học này, người học có thể:
Hiểu bản chất Báo cáo Lưu chuyển Tiền tệ (Direct Cash Flow Statement): Nắm vững nguyên lý phản ánh dòng tiền thực thu (Cash Inflows) và thực chi (Cash Outflows) trong kỳ từ 3 hoạt động: Kinh doanh, Đầu tư, và Tài chính.
Xây dựng Thuật toán SQL Đối ứng Bút toán (Counterpart Accounting Entry Algorithm): Lập trình truy vấn SQL phân tích các cặp tài khoản đối ứng với Tài khoản Tiền (111, 112) để tự động gắn nhãn mã dòng tiền.
Quy chuẩn hóa Mã chỉ tiêu Lưu chuyển Tiền tệ: Thiết lập bảng định ánh xạ (mapping matrix) giữa cặp tài khoản Nợ/Có với các chỉ tiêu tiền tệ theo chuẩn mực kế toán.
Đóng gói Python API Báo cáo Tiền tệ: Lập trình hàm Python tính toán Dòng tiền thuần trong kỳ, tự động đối soát với biến động tiền trên Bảng Cân đối Kế toán:
Báo cáo Lưu chuyển Tiền tệ theo phương pháp trực tiếp xác định dòng tiền vào/ra bằng cách phân tích trực tiếp các bút toán ghi Nợ hoặc ghi Có vào tài khoản Tiền tệ (TK 111 - Tiền mặt, TK 112 - Tiền gửi Ngân hàng).
SƠ ĐỒ THUẬT TOÁN ĐỐI ỨNG DÒNG TIỀN
┌────────────────────────────────────────────────────────┐
│ TÀI KHỎAN TIỀN TỆ (TK 111, 112) │
│ - Ghi Nợ (Debit) : Dòng tiền VÀO (+) │
│ - Ghi Có (Credit) : Dòng tiền RA (-) │
└───────────────────────────┬────────────────────────────┘
│
Tìm Tài khoản Đối ứng (Counterpart Account)
│
┌─────────────────────────────────┼─────────────────────────────────┐
▼ ▼ ▼
┌─────────────────────────┐ ┌─────────────────────────┐ ┌─────────────────────────┐
│ HOẠT ĐỘNG KINH DOANH │ │ HOẠT ĐỘNG ĐẦU TƯ │ │ HOẠT ĐỘNG TÀI CHÍNH │
├─────────────────────────┤ ├─────────────────────────┤ ├─────────────────────────┤
│ • Thu từ bán hàng (511) │ │ • Mua sắm TSCĐ (211) │ │ • Thu từ góp vốn (411) │
│ • Chi trả người bán(331)│ │ • Chi cho vay/đầu tư │ │ • Thu từ vay nợ (341) │
│ • Chi trả lương (334) │ │ • Thu hồi đầu tư, lãi │ │ • Chi trả nợ gốc vay │
│ • Chi nộp thuế (333) │ │ cho vay (515) │ │ • Chi trả cổ tức/lãi │
└─────────────────────────┘ └─────────────────────────┘ └─────────────────────────┘
| Mã STT | Chỉ tiêu | Cặp Bút toán (Tiền vs TK Đối ứng) | Bản chất Dòng tiền |
| 01 | Tiền thu từ bán hàng, cung cấp dịch vụ | Nợ 111, 112 / Có 511, 131, 515 | + (Kinh doanh) |
| 02 | Tiền chi trả cho người cung cấp | Có 111, 112 / Nợ 331, 152, 156, 627 | - (Kinh doanh) |
| 03 | Tiền chi trả cho người lao động | Có 111, 112 / Nợ 334 | - (Kinh doanh) |
| 04 | Tiền chi trả lãi vay | Có 111, 112 / Nợ 635 | - (Kinh doanh) |
| 05 | Tiền chi nộp thuế TNDN | Có 111, 112 / Nợ 3334 | - (Kinh doanh) |
| 21 | Tiền chi mua sắm, xây dựng TSCĐ | Có 111, 112 / Nợ 211, 241 | - (Đầu tư) |
| 31 | Tiền thu từ phát hành cổ phiếu, nhận vốn góp | Nợ 111, 112 / Có 411 | + (Tài chính) |
| 33 | Tiền thu từ đi vay | Nợ 111, 112 / Có 341 | + (Tài chính) |
| 34 | Tiền chi trả nợ gốc vay | Có 111, 112 / Nợ 341 | - (Tài chính) |
Dưới đây là mã nguồn Python hoàn chỉnh sử dụng SQL để quét các cặp bút toán đối ứng của tài khoản tiền và tổng hợp Báo cáo Lưu chuyển Tiền tệ.
import sqlite3
import pandas as pd
def get_direct_cash_flow(from_date: str, to_date: str, db_name: str = "accounting_mini.db") -> dict:
"""
Trích xuất Báo cáo Lưu chuyển Tiền tệ theo phương pháp trực tiếp trong khoảng [from_date, to_date].
"""
conn = sqlite3.connect(db_name)
# 1. Truy vấn các dòng nhật ký dính dán tới TK Tiền (111, 112) cùng TK Đối ứng trong từng giao dịch
# Sử dụng Self-Join để tìm tài khoản đối ứng trong cùng một transaction_id
query_cash_transactions = """
SELECT
t.transaction_id,
t.entry_date,
j_cash.account_code AS cash_account,
j_cash.debit_amount AS cash_debit,
j_cash.credit_amount AS cash_credit,
j_counter.account_code AS counterpart_account,
j_counter.debit_amount AS counterpart_debit,
j_counter.credit_amount AS counterpart_credit
FROM journal_entries j_cash
INNER JOIN transactions t ON j_cash.transaction_id = t.transaction_id
INNER JOIN journal_entries j_counter ON j_cash.transaction_id = j_counter.transaction_id
AND j_cash.account_code != j_counter.account_code
WHERE (j_cash.account_code LIKE '111%' OR j_cash.account_code LIKE '112%')
AND t.entry_date >= ? AND t.entry_date <= ?
"""
df_raw = pd.read_sql_query(query_cash_transactions, conn, params=[from_date, to_date])
# 2. Truy vấn số dư Tiền ĐẦU KỲ và CUỐI KỲ để kiểm tra đối soát
query_cash_balance = """
SELECT
COALESCE(SUM(CASE WHEN t.entry_date < ? THEN j.debit_amount - j.credit_amount ELSE 0 END), 0.0) AS open_cash,
COALESCE(SUM(CASE WHEN t.entry_date <= ? THEN j.debit_amount - j.credit_amount ELSE 0 END), 0.0) AS close_cash
FROM journal_entries j
INNER JOIN transactions t ON j.transaction_id = t.transaction_id
WHERE j.account_code LIKE '111%' OR j.account_code LIKE '112%'
"""
cursor = conn.cursor()
cursor.execute(query_cash_balance, (from_date, to_date))
open_cash, close_cash = cursor.fetchone()
conn.close()
# 3. Phân loại dòng tiền dựa vào tài khoản đối ứng
cf_data = {
"01": 0.0, # Thu từ bán hàng, dịch vụ
"02": 0.0, # Chi trả cho người cung cấp
"03": 0.0, # Chi trả cho người lao động
"04": 0.0, # Chi trả lãi vay
"05": 0.0, # Chi nộp thuế TNDN
"07": 0.0, # Chi khác cho hoạt động kinh doanh
"21": 0.0, # Chi mua sắm TSCĐ
"31": 0.0, # Thu từ nhận vốn góp
"33": 0.0, # Thu từ đi vay
"34": 0.0 # Chi trả nợ gốc vay
}
for _, row in df_raw.iterrows():
c_debit = row["cash_debit"]
c_credit = row["cash_credit"]
counter_code = str(row["counterpart_account"])
# DÒNG TIỀN VÀO (Cash Debit > 0)
if c_debit > 0:
if counter_code.startswith(('511', '131', '521')):
cf_data["01"] += c_debit
elif counter_code.startswith('411'):
cf_data["31"] += c_debit
elif counter_code.startswith('341'):
cf_data["33"] += c_debit
# DÒNG TIỀN RA (Cash Credit > 0)
elif c_credit > 0:
if counter_code.startswith(('331', '152', '156', '632')):
cf_data["02"] -= c_credit
elif counter_code.startswith(('334', '622')):
cf_data["03"] -= c_credit
elif counter_code.startswith('635'):
cf_data["04"] -= c_credit
elif counter_code.startswith(('3334', '821')):
cf_data["05"] -= c_credit
elif counter_code.startswith(('211', '241')):
cf_data["21"] -= c_credit
elif counter_code.startswith('341'):
cf_data["34"] -= c_credit
elif counter_code.startswith(('641', '642')):
cf_data["07"] -= c_credit
# 4. TỔNG HỢP CÁC LƯU CHUYỂN TIỀN THUẦN
# I. Lưu chuyển tiền từ Hoạt động Kinh doanh
net_cf_operating = (cf_data["01"] + cf_data["02"] + cf_data["03"] +
cf_data["04"] + cf_data["05"] + cf_data["07"])
# II. Lưu chuyển tiền từ Hoạt động Đầu tư
net_cf_investing = cf_data["21"]
# III. Lưu chuyển tiền từ Hoạt động Tài chính
net_cf_financing = cf_data["31"] + cf_data["33"] + cf_data["34"]
# IV. Lưu chuyển tiền thuần trong kỳ (50 = 20 + 30 + 40)
total_net_cash_flow = net_cf_operating + net_cf_investing + net_cf_financing
# 5. ĐÓNG GÓI BÁO CÁO
cf_structure = [
{"Mã số": "01", "Chỉ tiêu": "1. Tiền thu từ bán hàng, cung cấp dịch vụ", "Số tiền": cf_data["01"]},
{"Mã số": "02", "Chỉ tiêu": "2. Tiền chi trả cho người cung cấp hàng hóa, dịch vụ", "Số tiền": cf_data["02"]},
{"Mã số": "03", "Chỉ tiêu": "3. Tiền chi trả cho người lao động", "Số tiền": cf_data["03"]},
{"Mã số": "04", "Chỉ tiêu": "4. Tiền chi trả lãi vay", "Số tiền": cf_data["04"]},
{"Mã số": "05", "Chỉ tiêu": "5. Tiền chi nộp thuế TNDN", "Số tiền": cf_data["05"]},
{"Mã số": "07", "Chỉ tiêu": "6. Tiền chi khác cho hoạt động kinh doanh", "Số tiền": cf_data["07"]},
{"Mã số": "20", "Chỉ tiêu": "I. LƯU CHUYỂN TIỀN THUẦN TỪ HOẠT ĐỘNG KINH DOANH", "Số tiền": net_cf_operating},
{"Mã số": "21", "Chỉ tiêu": "1. Tiền chi mua sắm, xây dựng TSCĐ", "Số tiền": cf_data["21"]},
{"Mã số": "30", "Chỉ tiêu": "II. LƯU CHUYỂN TIỀN THUẦN TỪ HOẠT ĐỘNG ĐẦU TƯ", "Số tiền": net_cf_investing},
{"Mã số": "31", "Chỉ tiêu": "1. Tiền thu từ phát hành cổ phiếu, nhận vốn góp", "Số tiền": cf_data["31"]},
{"Mã số": "33", "Chỉ tiêu": "2. Tiền thu từ đi vay", "Số tiền": cf_data["33"]},
{"Mã số": "34", "Chỉ tiêu": "3. Tiền chi trả nợ gốc vay", "Số tiền": cf_data["34"]},
{"Mã số": "40", "Chỉ tiêu": "III. LƯU CHUYỂN TIỀN THUẦN TỪ HOẠT ĐỘNG TÀI CHÍNH", "Số tiền": net_cf_financing},
{"Mã số": "50", "Chỉ tiêu": "LƯU CHUYỂN TIỀN THUẦN TRONG KỲ (50 = 20 + 30 + 40)", "Số tiền": total_net_cash_flow},
{"Mã số": "60", "Chỉ tiêu": "Tiền và tương đương tiền đầu kỳ", "Số tiền": open_cash},
{"Mã số": "70", "Chỉ tiêu": "TIỀN VÀ TƯƠNG ĐƯƠNG TIỀN CUỐI KỲ (70 = 50 + 60)", "Số tiền": total_net_cash_flow + open_cash},
]
df_cf = pd.DataFrame(cf_structure)
# Đối soát kiểm tra tính nhất quán dữ liệu
calc_closing_cash = open_cash + total_net_cash_flow
is_reconciled = abs(calc_closing_cash - close_cash) < 1e-5
return {
"df": df_cf,
"from_date": from_date,
"to_date": to_date,
"net_cash_flow": total_net_cash_flow,
"open_cash": open_cash,
"close_cash": close_cash,
"is_reconciled": is_reconciled
}
Dưới đây là kịch bản chạy trích xuất Báo cáo Lưu chuyển Tiền tệ tháng 09/2026 và kiểm tra tính nhất quán đối soát với số dư tài khoản Tiền.
if __name__ == "__main__":
cf_result = get_direct_cash_flow("2026-09-01", "2026-09-30")
df_cf = cf_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ÁO CÁO LƯU CHUYỂN TIỀN TỆ (PHƯƠNG PHÁP TRỰC TIẾP) ")
print(f" Kỳ: {cf_result['from_date']} đến {cf_result['to_date']} ")
print("==========================================================================")
print(df_cf.to_string(index=False))
print("--------------------------------------------------------------------------")
print(f" Tiền đầu kỳ (Sổ kế toán) : {cf_result['open_cash']:,.0f} VNĐ")
print(f" Lưu chuyển tiền thuần : {cf_result['net_cash_flow']:,.0f} VNĐ")
print(f" Tiền cuối kỳ (Tính toán) : {cf_result['open_cash'] + cf_result['net_cash_flow']:,.0f} VNĐ")
print(f" Tiền cuối kỳ (Sổ kế toán) : {cf_result['close_cash']:,.0f} VNĐ")
print("--------------------------------------------------------------------------")
if cf_result["is_reconciled"]:
print("[XÁC NHẬN] ĐỐI SOÁT DÒNG TIỀN HOÀN TOÀN CHÍNH XÁC! (Khớp số dư Sổ cái Tiền)")
else:
print("[CẢNH BÁO LỖI] Có chênh lệch giữa Dòng tiền thuần và Biến động tài khoản Tiền!")
print("==========================================================================")
==========================================================================
BÁO CÁO LƯU CHUYỂN TIỀN TỆ (PHƯƠNG PHÁP TRỰC TIẾP)
Kỳ: 2026-09-01 đến 2026-09-30
=====================================================================
Mã số Chỉ tiêu Số tiền
01 1. Tiền thu từ bán hàng, cung cấp dịch vụ 115,000,000
02 2. Tiền chi trả cho người cung cấp hàng hóa... 0
03 3. Tiền chi trả cho người lao động 0
04 4. Tiền chi trả lãi vay 0
05 5. Tiền chi nộp thuế TNDN 0
07 6. Tiền chi khác cho hoạt động kinh doanh - 2,000,000
20 I. LƯU CHUYỂN TIỀN THUẦN TỪ HOẠT ĐỘNG KINH... 113,000,000
21 1. Tiền chi mua sắm, xây dựng TSCĐ 0
30 II. LƯU CHUYỂN TIỀN THUẦN TỪ HOẠT ĐỘNG ĐẦU TƯ 0
31 1. Tiền thu từ phát hành cổ phiếu, nhận vốn... 0
33 2. Tiền thu từ đi vay 0
34 3. Tiền chi trả nợ gốc vay 0
40 II. LƯU CHUYỂN TIỀN THUẦN TỪ HOẠT ĐỘNG TÀI... 0
50 LƯU CHUYỂN TIỀN THUẦN TRONG KỲ (50 = 20+30+40) 113,000,000
60 Tiền và tương đương tiền đầu kỳ 0
70 TIỀN VÀ TƯƠNG ĐƯƠNG TIỀN CUỐI KỲ (70 = 50 + 60) 113,000,000
---------------------------------------------------------------------
Tiền đầu kỳ (Sổ kế toán) : 0 VNĐ
Lưu chuyển tiền thuần : 113,000,000 VNĐ
Tiền cuối kỳ (Tính toán) : 113,000,000 VNĐ
Tiền cuối kỳ (Sổ kế toán) : 113,000,000 VNĐ
--------------------------------------------------------------------------
[XÁC NHẬN] ĐỐI SOÁT DÒNG TIỀN HOÀN TOÀN CHÍNH XÁC! (Khớp số dư Sổ cái Tiền)
==========================================================================
Giao dịch rút tiền mặt từ Ngân hàng về quỹ ($Nợ\ TK\ 111 / Có\ TK\ 112$) sẽ làm ảnh hưởng như thế nào đến Báo cáo Lưu chuyển Tiền tệ theo phương pháp trực tiếp?
A. Làm tăng Dòng tiền Kinh doanh 111 và giảm Dòng tiền Kinh doanh 112.
B. Không làm thay đổi Lưu chuyển tiền thuần trong kỳ (chỉ là chuyển dịch nội bộ giữa các khoản tương đương tiền).
C. Làm tăng Dòng tiền từ Hoạt động Tài chính.
Hãy cải tiến câu lệnh SQL trong hàm get_direct_cash_flow() để loại trừ các bút toán chuyển tiền nội bộ ($Nợ\ TK\ 111 / Có\ TK\ 112$ hoặc ngược lại $Nợ\ TK\ 112 / Có\ TK\ 111$), đảm bảo dòng tiền vào và dòng tiền ra không bị ghi nhận trùng lặp.
Bài tập 1:
Đáp án đúng là B. Chuyển tiền giữa Tiền mặt và Tiền gửi Ngân hàng không tạo ra dòng tiền vào/ra ròng khỏi doanh nghiệp.
Bài tập 2:
-- Bổ sung điều kiện loại trừ tài khoản đối ứng cũng thuộc nhóm Tiền tệ
WHERE (j_cash.account_code LIKE '111%' OR j_cash.account_code LIKE '112%')
AND NOT (j_counter.account_code LIKE '111%' OR j_counter.account_code LIKE '112%')
AND t.entry_date >= ? AND t.entry_date <= ?