DHDataHọc
Lý thuyếtBài 1/85 phút đọc+10 XP

P&L từ dữ liệu giao dịch

Bạn sẽ học được

  • Dựng báo cáo lãi lỗ (P&L) từ dữ liệu giao dịch.
  • Phân loại doanh thu, chi phí nhất quán bằng bảng ánh xạ tài khoản.
  • Tính doanh thu thuần sau giảm giá và hoàn tiền.
  • Đọc biên lợi nhuận gộp và biên hoạt động.
  • Tránh những lỗi phân loại làm sai toàn bộ báo cáo.

Khái niệm cốt lõi

P&L là báo cáo lãi lỗ trong một kỳ:

code
Doanh thu thuần
− Giá vốn hàng bán (COGS)
= Lợi nhuận gộp
− Chi phí hoạt động (opex)
= Lợi nhuận hoạt động

Mỗi dòng là một nhóm tài khoản. Điều quan trọng không phải công thức — công thức đơn giản — mà là phân loại nhất quán: cùng một loại giao dịch phải vào cùng một dòng, ở mọi kỳ, với mọi người làm báo cáo.

Doanh thu thuần

Doanh thu trên báo cáo không phải tổng tiền khách trả:

code
Doanh thu thuần = doanh thu gộp − giảm giá − hoàn tiền

Ví dụ một tháng:

DòngSố tiền (triệu đồng)
Doanh thu gộp2.000
Giảm giá120
Hoàn tiền80
Doanh thu thuần1.800

Bỏ giảm giá và hoàn tiền ra khỏi báo cáo là cách phổ biến nhất để làm đẹp doanh thu. Cả hai đều là chi phí thật.

Lưu ý về thuế GTGT: doanh thu trên P&L thường không gồm VAT. Nếu bảng giao dịch lưu giá đã có VAT, phải tách ra trước khi tính.

Phân loại bằng bảng ánh xạ

Đừng viết CASE dài trong mọi báo cáo. Tạo bảng ánh xạ tài khoản:

code
CREATE TABLE account_map (
  account_code text PRIMARY KEY,
  pnl_line     text NOT NULL,   -- 'revenue' | 'cogs' | 'opex'
  sort_order   int  NOT NULL
);

INSERT INTO account_map VALUES
  ('4000', 'revenue', 10),
  ('4100', 'revenue', 20),
  ('5000', 'cogs',    30),
  ('6000', 'opex',    40),
  ('6100', 'opex',    50);

Khi có tài khoản mới, thêm một dòng vào bảng thay vì sửa mọi truy vấn. Đây là cách duy nhất để nhiều người dùng chung một định nghĩa.

Truy vấn P&L theo tháng

code
SELECT
  date_trunc('month', t.posted_at)::date AS thang,
  SUM(t.amount) FILTER (WHERE m.pnl_line = 'revenue') AS doanh_thu,
  SUM(t.amount) FILTER (WHERE m.pnl_line = 'cogs') AS cogs,
  SUM(t.amount) FILTER (WHERE m.pnl_line = 'revenue')
    - ABS(SUM(t.amount) FILTER (WHERE m.pnl_line = 'cogs')) AS loi_nhuan_gop,
  SUM(t.amount) FILTER (WHERE m.pnl_line = 'opex') AS opex
FROM transactions t
JOIN account_map m ON m.account_code = t.account_code
WHERE t.posted_at >= date_trunc('year', CURRENT_DATE)
GROUP BY 1
ORDER BY 1;

Quy ước dấu rất quan trọng: chọn một quy ước và giữ nguyên. Thông thường doanh thu dương, chi phí âm; khi báo cáo thì đảo dấu chi phí để hiển thị số dương. Trộn hai quy ước làm tổng sai mà không báo lỗi.

Đọc biên lợi nhuận

code
Biên gộp       = lợi nhuận gộp / doanh thu thuần
Biên hoạt động = lợi nhuận hoạt động / doanh thu thuần

Với doanh thu thuần 1.800 triệu, COGS 1.080 triệu, opex 450 triệu:

DòngSố tiềnBiên
Doanh thu thuần1.800100%
COGS1.08060%
Lợi nhuận gộp72040%
Opex45025%
Lợi nhuận hoạt động27015%

Biên gộp 40% nói rằng mỗi 100 đồng doanh thu còn 40 đồng để trả chi phí cố định và lãi. Biên hoạt động 15% nói rằng sau tất cả chi phí vận hành còn 15 đồng.

So sánh biên giữa các kỳ chỉ có ý nghĩa khi cách phân loại không đổi. Nếu chuyển một khoản từ COGS sang opex, biên gộp tăng nhưng biên hoạt động không đổi — nhìn riêng biên gộp sẽ tưởng công ty tốt lên.

Những lỗi phân loại hay gặp

Vấn đềHệ quảCách xử lý
Tính doanh thu gộp, bỏ giảm giá/hoànDoanh thu và lợi nhuận bị thổi phồngLuôn trừ cả hai
Gồm VAT trong doanh thuDoanh thu tăng ảo 8–10%Tách VAT
Đưa lương bán hàng vào COGSBiên gộp thay đổi tuỳ cách chiaChốt quy ước, ghi tài liệu
Ghi nhận marketplace theo grossDoanh thu và chi phí cùng phồngGhi net nếu chỉ là trung gian
Trộn kế toán dồn tích và tiền mặtDoanh thu kỳ này lẫn kỳ khácChọn một cơ sở
Đổi cách phân loại giữa nămChuỗi thời gian gãyGiữ nguyên hoặc trình bày lại toàn bộ

Kiểm tra chéo trước khi công bố

Ba kiểm tra nhanh:

  1. Tổng doanh thu báo cáo = tổng hóa đơn − giảm giá − hoàn tiền trong cùng kỳ.
  2. Tổng chi phí báo cáo = tổng giao dịch chi phí theo bảng ánh xạ, không sót tài khoản.
  3. Lợi nhuận tính từ P&L khớp với lợi nhuận tính từ sổ cái — nếu lệch, có tài khoản chưa được ánh xạ.

Tài khoản chưa ánh xạ là lỗi nguy hiểm nhất vì nó âm thầm biến mất khỏi báo cáo: JOIN bỏ qua dòng không khớp. Dùng LEFT JOIN và đếm dòng NULL để phát hiện.

code
SELECT t.account_code, COUNT(*) AS so_giao_dich, SUM(t.amount) AS tong
FROM transactions t
LEFT JOIN account_map m ON m.account_code = t.account_code
WHERE m.account_code IS NULL
GROUP BY 1
ORDER BY 3;

Thực hành

  1. Tạo bảng ánh xạ cho năm tài khoản của một công ty nhỏ.
  2. Viết truy vấn P&L theo tháng với doanh thu thuần.
  3. Tính biên gộp và biên hoạt động từ kết quả.
  4. Kiểm tra tài khoản chưa ánh xạ.
  5. Giải thích vì sao chuyển chi phí từ COGS sang opex làm biên gộp tăng.

Lỗi thường gặp

Triệu chứngNguyên nhânCách sửa
Doanh thu cao hơn tiền thuKhông trừ giảm giá, hoàn tiềnTính doanh thu thuần
Lợi nhuận không khớp sổ cáiThiếu tài khoản trong ánh xạLEFT JOIN, xử lý NULL
Biên gộp nhảy bất thườngPhân loại đổiChốt quy ước
Doanh thu tăng ảoGồm VATTách thuế
Số âm khó hiểuTrộn quy ước dấuChốt quy ước dấu
Marketplace lãi lớnGhi gross thay vì netGhi net

Tóm tắt

  • P&L đi từ doanh thu thuần qua giá vốn đến lợi nhuận hoạt động.
  • Doanh thu thuần phải trừ giảm giá và hoàn tiền.
  • Phân loại bằng bảng ánh xạ, không bằng CASE rải rác.
  • Chốt quy ước dấu và giữ nguyên.
  • Kiểm tra tài khoản chưa ánh xạ bằng LEFT JOIN.

Dùng ← → để chuyển bài