Sau khi hoàn thành bài học này, người học có thể:
Hiểu mô hình dữ liệu kế toán: Hiểu cách chuyển đổi kiến thức định khoản (Nợ/Có) thành các bảng dữ liệu có quan hệ (Relational Database Schema).
Thiết kế cấu trúc Bảng chuẩn: Xây dựng được 3 bảng nền tảng: Bảng Tài khoản (accounts), Bảng Bút toán (transactions), và Bảng Chi tiết Định khoản (journal_entries).
Khởi tạo Database bằng Python: Viết script Python để tự động tạo cơ sở dữ liệu SQLite và nạp Hệ thống Tài khoản chuẩn.
Để đảm bảo nguyên tắc Một bút toán có thể bao gồm nhiều dòng Nợ và nhiều dòng Có (nhưng tổng Nợ phải luôn bằng tổng Có), chúng ta cần tách cấu trúc giao dịch thành 2 bảng có quan hệ 1 - N (One-to-Many):
transactions (Bút toán tổng thể): Lưu thông tin chung như Ngày, Số chứng từ, Diễn giải.
journal_entries (Chi tiết định khoản): Lưu chi tiết từng dòng Nợ/Có của bút toán đó.
┌─────────────────────────┐ ┌─────────────────────────┐ ┌─────────────────────────┐
│ ACCOUNTS │ │ TRANSACTIONS │ │ JOURNAL_ENTRIES │
├─────────────────────────┤ ├─────────────────────────┤ ├─────────────────────────┤
│ PK account_code (TEXT) │1 N│ PK transaction_id (INT)│1 N│ PK entry_id (INTEGER) │
│ account_name (TEXT) ├───────┼─ entry_date (TEXT) ├───────┼─ FK transaction_id (INT)│
│ account_type (TEXT) │ │ ref_no (TEXT) │ │ FK account_code (TEXT) │
│ normal_balance(TEXT)│ │ description (TEXT) │ │ debit_amount (REAL) │
└─────────────────────────┘ └─────────────────────────┘ │ credit_amount (REAL) │
└─────────────────────────┘
accounts (Danh mục Hệ thống Tài khoản)Lưu trữ danh sách các Tài khoản kế toán (TK 111, 112, 131, 331, 511, 642...).
account_code (TEXT, PRIMARY KEY): Mã tài khoản (VD: '111', '112').
account_name (TEXT, NOT NULL): Tên tài khoản (VD: 'Tiền mặt').
account_type (TEXT, NOT NULL): Nhóm tài khoản ('ASSET', 'LIABILITY', 'EQUITY', 'REVENUE', 'EXPENSE').
normal_balance (TEXT, NOT NULL): Số dư bình thường ('DEBIT' hoặc 'CREDIT').
transactions (Chứng từ / Bút toán Tổng thể)Lưu trữ thông tin định danh của từng sự kiện kinh tế phát sinh.
transaction_id (INTEGER, PRIMARY KEY AUTOINCREMENT): Mã định danh bút toán.
entry_date (TEXT, NOT NULL): Ngày ghi sổ (YYYY-MM-DD).
ref_no (TEXT): Số hóa đơn/chứng từ (VD: 'PT001', 'PC002').
description (TEXT): Diễn giải nội dung giao dịch.
journal_entries (Chi tiết Định khoản Nợ / Có)Lưu vết từng dòng ghi Nợ hoặc ghi Có cho từng tài khoản cụ thể.
entry_id (INTEGER, PRIMARY KEY AUTOINCREMENT): Mã chi tiết dòng.
transaction_id (INTEGER, FOREIGN KEY): Liên kết tới transactions.
account_code (TEXT, FOREIGN KEY): Liên kết tới accounts.
debit_amount (REAL, DEFAULT 0.0): Số tiền ghi Nợ.
credit_amount (REAL, DEFAULT 0.0): Số tiền ghi Có.
Dưới đây là mã nguồn Python đầy đủ để kết nối SQLite, tạo cấu trúc bảng chuẩn và nạp sẵn Hệ thống Tài khoản Kế toán cơ bản.
import sqlite3
def init_accounting_db(db_name="accounting_mini.db"):
# Kết nối tới cơ sở dữ liệu SQLite (tự động tạo file nếu chưa có)
conn = sqlite3.connect(db_name)
cursor = conn.cursor()
# Bật tính năng ràng buộc khóa ngoại (Foreign Key)
cursor.execute("PRAGMA foreign_keys = ON;")
# 1. Tạo Bảng Hệ thống Tài khoản
cursor.execute("""
CREATE TABLE IF NOT EXISTS accounts (
account_code TEXT PRIMARY KEY,
account_name TEXT NOT NULL,
account_type TEXT NOT NULL,
normal_balance TEXT NOT NULL CHECK(normal_balance IN ('DEBIT', 'CREDIT'))
);
""")
# 2. Tạo Bảng Bút toán Tổng thể
cursor.execute("""
CREATE TABLE IF NOT EXISTS transactions (
transaction_id INTEGER PRIMARY KEY AUTOINCREMENT,
entry_date TEXT NOT NULL,
ref_no TEXT,
description TEXT NOT NULL
);
""")
# 3. Tạo Bảng Chi tiết Định khoản (Sổ Nhật ký chung chi tiết)
cursor.execute("""
CREATE TABLE IF NOT EXISTS journal_entries (
entry_id INTEGER PRIMARY KEY AUTOINCREMENT,
transaction_id INTEGER NOT NULL,
account_code TEXT NOT NULL,
debit_amount REAL DEFAULT 0.0 CHECK(debit_amount >= 0),
credit_amount REAL DEFAULT 0.0 CHECK(credit_amount >= 0),
FOREIGN KEY (transaction_id) REFERENCES transactions(transaction_id) ON DELETE CASCADE,
FOREIGN KEY (account_code) REFERENCES accounts(account_code)
);
""")
# 4. Nạp danh mục Tài khoản Kế toán mẫu (Chart of Accounts)
sample_accounts = [
('111', 'Tiền mặt', 'ASSET', 'DEBIT'),
('112', 'Tiền gửi Ngân hàng', 'ASSET', 'DEBIT'),
('131', 'Phải thu của khách hàng', 'ASSET', 'DEBIT'),
('156', 'Hàng hóa', 'ASSET', 'DEBIT'),
('211', 'Tài sản cố định hữu hình', 'ASSET', 'DEBIT'),
('214', 'Hao mòn tài sản cố định', 'ASSET', 'CREDIT'),
('331', 'Phải trả cho người bán', 'LIABILITY', 'CREDIT'),
('334', 'Phải trả người lao động', 'LIABILITY', 'CREDIT'),
('411', 'Vốn góp của chủ sở hữu', 'EQUITY', 'CREDIT'),
('421', 'Lợi nhuận sau thuế chưa phân phối', 'EQUITY', 'CREDIT'),
('511', 'Doanh thu bán hàng và cung cấp dịch vụ', 'REVENUE', 'CREDIT'),
('632', 'Giá vốn hàng bán', 'EXPENSE', 'DEBIT'),
('642', 'Chi phí quản lý doanh nghiệp', 'EXPENSE', 'DEBIT')
]
cursor.executemany("""
INSERT OR IGNORE INTO accounts (account_code, account_name, account_type, normal_balance)
VALUES (?, ?, ?, ?);
""", sample_accounts)
conn.commit()
conn.close()
print("-> Đã khởi tạo thành công Database Kế toán Mini với SQLite!")
# Chạy thử hàm khởi tạo
if __name__ == "__main__":
init_accounting_db()
Tại sao chúng ta phải chia làm 2 bảng transactions và journal_entries thay vì gộp chung vào 1 bảng duy nhất?
A. Để tiết kiệm dung lượng ổ cứng.
B. Để cho phép một nghiệp vụ kinh tế có thể định khoản Phức tạp (1 Nợ - Nhiều Có, hoặc Nhiều Nợ - 1 Có).
C. SQLite không hỗ trợ ghi nhận số tiền trên cùng 1 bảng.
Hãy bổ sung thêm 2 tài khoản kế toán sau vào danh mục sample_accounts trong đoạn code Python trên:
TK 133: Thuế GTGT được khấu trừ (Thuộc nhóm ASSET, số dư bình thường DEBIT).
TK 3331: Thuế GTGT phải nộp (Thuộc nhóm LIABILITY, số dư bình thường CREDIT).
Bài tập 1:
Đáp án đúng là B. Thiết kế chuẩn hóa (Normalization) chia làm 2 bảng giúp 1 giao dịch có thể chứa vô số dòng định khoản Nợ/Có khác nhau mà vẫn đảm bảo tính toàn vẹn dữ liệu.
Bài tập 2:
Bổ sung vào danh sách:
('133', 'Thuế GTGT được khấu trừ', 'ASSET', 'DEBIT'),
('3331', 'Thuế GTGT phải nộp', 'LIABILITY', 'CREDIT')