Mô hình sổ cái thanh toán dễ đối chiếu: hóa đơn và khoản thanh toán
Tìm hiểu cách thiết kế mô hình sổ cái thanh toán tách riêng hóa đơn, thanh toán, tín dụng và điều chỉnh để finance dễ dàng đối chiếu và kiểm toán tổng số.

Tại sao dữ liệu thanh toán không còn khớp
Với bộ phận tài chính, “đối chiếu” là một lời hứa đơn giản: các tổng trên báo cáo khớp với các bản ghi nguồn, và mọi con số đều có thể truy vết. Nếu tháng này báo cáo là $12,430 đã thu, bạn phải chỉ ra được các khoản thanh toán chính xác (và mọi khoản hoàn tiền), thấy chúng áp dụng cho hóa đơn nào, và giải thích mọi khác biệt bằng một bản ghi có ngày tháng.
Dữ liệu thanh toán thường ngừng khớp khi cơ sở dữ liệu lưu trữ kết quả thay vì sự thật. Những cột như paid_amount, balance, hoặc amount_due bị cập nhật theo thời gian bởi logic ứng dụng. Một lỗi, một lần thử lại, hoặc một “sửa” thủ công có thể âm thầm thay đổi lịch sử. Vài tuần sau, bảng invoice nói một hóa đơn là “paid”, nhưng các hàng payment không cộng lại khớp, hoặc xuất hiện một refund mà không có credit tương ứng.
Nguyên nhân phổ biến khác là trộn lẫn các loại tài liệu khác nhau. Một invoice không phải là một payment. Một credit memo không phải là một refund. Một adjustment không giống discount. Khi những thứ đó bị nhồi vào một hàng “transactions” với nhiều trường tùy chọn, báo cáo trở thành phỏng đoán và kiểm toán biến thành tranh luận.
Sự không phù hợp cơ bản rất đơn giản: ứng dụng thường quan tâm đến trạng thái hiện tại (“truy cập còn hoạt động không?”), còn tài chính quan tâm đến dấu vết (“điều gì đã xảy ra, khi nào và vì sao?”). Mô hình sổ cái thanh toán phải hỗ trợ cả hai, nhưng khả năng truy vết phải thắng thế.
Thiết kế hướng tới kết quả này:
- Tổng rõ ràng theo khách hàng, theo hóa đơn và theo kỳ kế toán
- Mọi thay đổi được ghi lại dưới dạng một hàng mới (không bị ghi đè)
- Chuỗi hoàn chỉnh từ hóa đơn tới các khoản thanh toán, credit, refund và điều chỉnh
- Khả năng tính lại tổng từ các mục thô và ra kết quả giống nhau
Ví dụ: nếu khách hàng thanh toán $100 rồi được cấp một credit $20, báo cáo của bạn nên hiển thị $100 đã thu, $20 được ghi có, và $80 ròng, mà không sửa đổi số tiền gốc trên hóa đơn.
Tách riêng hóa đơn, thanh toán, tín dụng và điều chỉnh
Nếu bạn muốn một mô hình sổ cái thanh toán có thể đối chiếu, hãy coi mỗi loại tài liệu là một kiểu sự kiện khác nhau. Trộn chúng vào một bảng “transactions” có vẻ gọn gàng, nhưng làm lu mờ ý nghĩa.
Một invoice là một yêu cầu: “khách hàng nợ chúng ta tiền.” Lưu nó như một tài liệu với header (khách hàng, số hóa đơn, ngày phát hành, ngày đáo hạn, loại tiền, tổng) và các dòng riêng (mua gì, số lượng, đơn giá, mã thuế). Lưu tổng header để tăng tốc là ổn, nhưng bạn luôn phải giải thích được tổng đó từ các dòng.
Một payment là chuyển động tiền: “tiền từ khách hàng về phía chúng ta.” Trong luồng thẻ, thường có authorization (ngân hàng duyệt) và capture (tiền thực tế được lấy). Nhiều hệ thống giữ authorization như bản ghi vận hành và chỉ đưa các payment đã capture vào sổ cái, để không làm phình báo cáo tiền mặt.
Một credit memo giảm số tiền khách hàng còn nợ mà chưa chắc đã trả tiền lại. Một refund là tiền mặt chảy ra. Chúng thường xảy ra cùng nhau nhưng không giống nhau.
- Invoice: tăng phải thu và doanh thu (hoặc doanh thu hoãn)
- Payment: tăng tiền mặt và giảm phải thu
- Credit memo: giảm phải thu
- Refund: giảm tiền mặt
Một adjustment là sửa lỗi do đội của bạn thực hiện khi thực tế không khớp với hồ sơ. Các adjustment cần ngữ cảnh để tài chính có thể tin tưởng. Lưu người tạo, thời điểm đăng, mã lý do, và một ghi chú ngắn. Ví dụ: “xóa 0.03 do làm tròn,” hoặc “di chuyển số dư hệ thống cũ.”
Quy tắc thực tế: hỏi “Cái này có tồn tại nếu không ai mắc lỗi không?” Invoice, payment, credit memo và refund vẫn tồn tại. Adjustment nên hiếm, được gắn nhãn rõ ràng và dễ xem xét.
Chọn mô hình sổ cái mà tài chính có thể kiểm toán
Một mô hình sổ cái thanh toán có thể đối chiếu bắt đầu với một ý tưởng: tài liệu mô tả điều đã xảy ra, và các bút toán ledger chứng minh các tổng. Một invoice, payment, hoặc credit memo là tài liệu. Sổ cái là tập các mục ghi cộng lại, chấm hết.
Documents vs postings (lưu cả hai)
Giữ lại các tài liệu (header và dòng hóa đơn, biên nhận thanh toán, credit memo) vì người ta cần đọc chúng. Nhưng đừng dựa vào tổng tài liệu như nguồn sự thật cho đối chiếu.
Thay vào đó, post mỗi tài liệu vào một bảng ledger dưới dạng một hoặc nhiều mục bất biến. Khi đó tài chính có thể tổng các mục theo account, customer, currency và posting date và luôn ra cùng một kết quả.
Mô hình đơn giản thân thiện cho kiểm toán tuân theo vài quy tắc:
- Các mục bất biến: không bao giờ chỉnh sửa số tiền đã post; thay đổi là các mục mới.
- Sự kiện posting rõ ràng: mỗi tài liệu tạo một batch posting với tham chiếu duy nhất.
- Logic cân bằng: các mục cộng lại đúng (thường debit = credit ở cấp công ty).
- Ngày tách biệt: lưu ngày tài liệu (khách hàng thấy) và ngày posting (vào báo cáo).
- Tham chiếu ổn định: lưu tham chiếu bên ngoài (số hóa đơn, ID processor) cùng với ID nội bộ.
Khóa tự nhiên vs ID thay thế
Dùng ID thay thế (surrogate IDs) cho join và hiệu năng, nhưng cũng lưu một khóa tự nhiên ổn định tồn tại qua di cư và import lại. Tài chính sẽ hỏi về “Invoice INV-10483” lâu sau khi ID trong DB thay đổi. Hãy coi số hóa đơn và ID nhà cung cấp (như payment processor charge ID) là trường quan trọng.
Hoàn tác mà không xoá lịch sử
Khi phải hoàn tác, đừng xoá hay ghi đè. Post một reversal: các mục mới phản chiếu số tiền gốc nhưng dấu ngược, liên kết trở lại posting gốc.
Ví dụ: một payment $100 áp sai hóa đơn thành hai bước: reverse posting sai áp, sau đó post áp đúng vào hóa đơn đúng.
Bản thiết kế schema từng bước (bảng và khoá)
Một mô hình sổ cái thanh toán đối chiếu đáng tin cậy hơn khi mỗi loại tài liệu có bảng riêng và bạn kết nối chúng bằng các bản ghi allocation rõ ràng (thay vì đoán mối quan hệ sau này).
Bắt đầu với một tập bảng lõi nhỏ, mỗi bảng có khoá chính rõ ràng (UUID hoặc bigserial) và các foreign key bắt buộc:
- customers:
customer_id(PK), cùng các định danh ổn định nhưexternal_ref(unique) - invoices:
invoice_id(PK),customer_id(FK),invoice_number(unique),issue_date,due_date,currency - invoice_lines:
invoice_line_id(PK),invoice_id(FK),line_type,description,qty,unit_price,tax_code,amount - payments:
payment_id(PK),customer_id(FK),payment_date,method,currency,gross_amount - credits:
credit_id(PK),customer_id(FK),credit_number(unique),credit_date,currency,amount
Rồi thêm các bảng giúp tổng số có thể kiểm toán: allocations. Một payment hoặc credit có thể chi trả cho nhiều hóa đơn, và một hóa đơn có thể được trả bởi nhiều payment.
Dùng các bảng join với khoá riêng (không chỉ khoá hợp thành):
- payment_allocations:
payment_allocation_id(PK),payment_id(FK),invoice_id(FK),allocated_amount,posted_at - credit_allocations:
credit_allocation_id(PK),credit_id(FK),invoice_id(FK),allocated_amount,posted_at
Cuối cùng, giữ adjustments riêng để tài chính thấy có gì đã thay đổi và vì sao. Bảng adjustments có thể tham chiếu record mục tiêu bằng invoice_id (nullable) và lưu delta amount, mà không ghi đè lịch sử.
Thêm các trường audit ở mọi nơi bạn post tiền:
created_at,created_byreason_code(write-off, rounding, goodwill, chargeback)source_system(manual, import, Stripe, support tool)
Credits, refunds và xóa nợ mà không làm vỡ tổng
Hầu hết vấn đề đối chiếu bắt đầu khi credits và refunds được ghi nhận như “negative payments,” hoặc khi write-off bị trộn vào dòng hóa đơn. Một mô hình sổ cái sạch giữ mỗi loại tài liệu như một bản ghi riêng, và nơi duy nhất chúng tương tác là qua các allocation rõ ràng.
Một credit nên cho thấy lý do bạn giảm khoản khách hàng nợ. Nếu nó áp cho một hóa đơn, ghi một credit memo đơn và allocate vào hóa đơn đó. Nếu áp cho nhiều hóa đơn, allocate cùng credit memo đó tới nhiều hóa đơn. Credit vẫn là một tài liệu duy nhất với nhiều allocation.
Refund là sự kiện giống payment nhưng hướng ngược lại — tiền mặt chảy ra — nên xử lý nó như một bản ghi riêng (thường liên kết đến payment gốc để tham chiếu), rồi allocate nó như payment. Điều này giữ dấu vết kiểm toán rõ ràng khi sao kê ngân hàng cho thấy cả incoming payment và outgoing refund.
Thanh toán một phần và credit một phần hoạt động tương tự: giữ tổng payment hoặc credit trên một hàng riêng, và dùng các hàng allocation để thể hiện bao nhiêu được áp cho mỗi hóa đơn.
Quy tắc posting ngăn chặn đếm đôi
Những quy tắc này loại bỏ hầu hết “khác biệt bí ẩn”:
- Không bao giờ lưu một negative payment. Dùng một record refund.
- Không bao giờ giảm tổng hóa đơn sau khi đã post. Dùng một credit memo hoặc adjustment.
- Post tài liệu một lần (với
posted_attimestamp) và không chỉnh sửa số tiền sau khi post. - Điều duy nhất thay đổi balance hóa đơn là tổng allocations đã post.
- Write-off là một adjustment có mã lý do, được allocate tới hóa đơn như một credit.
Thuế, phí, tiền tệ và lựa chọn làm tròn
Hầu hết vấn đề đối chiếu bắt nguồn từ các tổng bạn không thể tái tạo. Quy tắc an toàn nhất là: lưu các dòng thô đã tạo hóa đơn, và cũng lưu các tổng bạn đã hiển thị cho khách hàng.
Thuế và phí: giữ ở cấp dòng
Lưu thuế và phí theo từng dòng chứ không chỉ là trường tóm tắt ở cấp hóa đơn. Các sản phẩm khác nhau có thể chịu mức thuế khác nhau, phí có thể chịu thuế hoặc không, và miễn trừ thường áp dụng cho một phần hóa đơn. Nếu bạn chỉ lưu một tax_total, bạn sẽ gặp trường hợp không thể giải thích.
Giữ:
- Dòng thô (bán gì, qty, đơn giá, discount)
- Tổng tính cho dòng (line_subtotal, line_tax, line_total)
- Tổng tóm tắt hóa đơn (subtotal, tax_total, total)
- Mức thuế và loại thuế đã sử dụng
- Phí là các dòng riêng (ví dụ: “Payment processing fee”)
Điều này cho phép tài chính xây dựng lại tổng và xác nhận thuế được tính cùng cách mỗi lần.
Đa tiền tệ: lưu điều gì đã xảy ra và cách bạn báo cáo
Nếu hỗ trợ nhiều tiền tệ, ghi cả giá trị theo tiền tệ giao dịch và giá trị theo tiền tệ báo cáo. Tối thiểu nên có: currency_code trên mọi tài liệu tiền tệ, một fx_rate dùng khi posting, và các số báo cáo riêng (ví dụ, amount_reporting) nếu sổ sách đóng bằng một loại tiền.
Ví dụ: khách hàng được lập hoá đơn 100.00 EUR cộng 20.00 EUR VAT. Lưu các dòng và tổng bằng EUR, kèm fx_rate dùng khi post hóa đơn, và tổng đã quy đổi cho báo cáo.
Làm tròn cần xử lý riêng. Chọn một quy tắc làm tròn (theo dòng hoặc theo hóa đơn) và bám theo nó. Khi làm tròn tạo ra sai số, ghi nó rõ ràng như một dòng adjustment làm tròn thay vì lặng lẽ sửa tổng.
Trạng thái, ngày posting và những gì không nên lưu
Đối chiếu rối rắm khi một “trạng thái” được dùng như lối tắt cho sự thật kế toán. Hãy coi trạng thái là nhãn workflow, và coi các mục ledger đã post là nguồn sự thật.
Làm cho trạng thái nghiêm ngặt và tẻ nhạt. Mỗi trạng thái nên trả lời: tài liệu này đã có thể ảnh hưởng tới tổng chưa?
- Draft: chỉ nội bộ, chưa post, không xuất hiện báo cáo
- Issued: đã chốt và gửi, sẵn sàng để post (hoặc đã post)
- Void: bị hủy; nếu đã post thì phải reverse
- Paid: đã được thanh toán đầy đủ bằng các payments và credits đã post
- Refunded: tiền đã được trả lại qua một refund đã post
Ngày còn quan trọng hơn nhiều đội nghĩ. Tài chính sẽ hỏi “Món này thuộc tháng nào?” và câu trả lời không nên phụ thuộc vào log UI.
issued_at: khi hóa đơn hoàn chỉnhposted_at: khi nó xuất hiện trong báo cáo kế toánsettled_at: khi tiền về hoặc payment được xác nhậnvoided_at/refunded_at: khi hoàn tác có hiệu lực
Những gì không nên lưu như chân lý: các số suy ra bạn không thể tái tạo từ ledger. Các trường như balance_due, is_overdue, và customer_lifetime_value ổn nếu chỉ là cache, miễn là bạn luôn có thể tính lại từ invoices, payments, credits, allocations và adjustments.
Một ví dụ nhỏ: retry payment gọi gateway hai lần. Nếu không có idempotency_key, bạn lưu hai payment, đánh dấu hóa đơn “paid”, rồi tài chính thấy thừa $100 tiền mặt. Lưu idempotency_key cho mỗi lần thử charge bên ngoài và từ chối trùng ở mức database.
Báo cáo tài chính mong đợi từ ngày đầu
Mô hình sổ cái thanh toán chứng minh giá trị khi tài chính có thể trả nhanh các câu hỏi cơ bản và luôn ra cùng một tổng.
Hầu hết đội bắt đầu với:
- Bảng tuổi nợ phải thu (accounts receivable aging): số dư còn mở theo khách hàng và các nhóm tuổi (0-30, 31-60, v.v.)
- Tiền thu: tiền thu theo ngày, tuần, tháng, dựa trên posting date của payments
- Doanh thu vs tiền mặt: invoices đã post so với payments đã post
- Dấu vết kiểm toán cho xuất khẩu: đường dẫn drill-back từ một dòng GL export tới tài liệu và các hàng allocation đã tạo nó
Aging là nơi allocations quan trọng nhất. Aging không phải là “tổng hóa đơn trừ tổng thanh toán.” Là “còn bao nhiêu mở trên từng hóa đơn tại một thời điểm.” Điều đó yêu cầu lưu cách mỗi payment, credit hay adjustment được áp cho hóa đơn cụ thể và khi các allocation đó được post.
Tiền thu nên được dẫn dắt bởi bảng payments, không phải trạng thái hóa đơn. Khách hàng có thể trả sớm, trả chậm hoặc trả một phần.
Doanh thu vs tiền mặt là lý do invoice và payment phải tách biệt. Ví dụ: bạn phát hành hóa đơn $1,000 vào 30/03, nhận $600 vào 05/04, và phát credit $100 vào 20/04. Doanh thu thuộc về tháng 3 (posting của invoice), tiền mặt thuộc về tháng 4 (posting của payment), và credit giảm phải thu khi nó được post. Allocations là thứ kết nối mọi thứ.
Ví dụ tình huống: một khách hàng, bốn loại tài liệu
Một khách hàng, một tháng, bốn loại tài liệu. Mỗi tài liệu được lưu một lần, và tiền được chuyển qua một bảng allocation (còn gọi là “applications”). Điều đó làm cho số dư cuối cùng dễ tính lại và dễ kiểm toán.
Giả sử khách hàng C-1001 (Acme Co.).
Các bản ghi bạn tạo
invoices
| invoice_id | customer_id | invoice_date | posted_at | currency | total |
|---|---|---|---|---|---|
| INV-10 | C-1001 | 2026-01-05 | 2026-01-05 | USD | 120.00 |
payments
| payment_id | customer_id | received_at | posted_at | method | amount |
|---|---|---|---|---|---|
| PAY-77 | C-1001 | 2026-01-10 | 2026-01-10 | card | 70.00 |
credits (credit memo, goodwill credit, v.v.)
| credit_id | customer_id | credit_date | posted_at | reason | amount |
|---|---|---|---|---|---|
| CR-5 | C-1001 | 2026-01-12 | 2026-01-12 | service issue | 20.00 |
adjustments (sửa sau, không phải bán mới)
| adjustment_id | customer_id | adjustment_date | posted_at | note | amount |
|---|---|---|---|---|---|
| ADJ-3 | C-1001 | 2026-01-15 | 2026-01-15 | underbilled fee | 5.00 |
allocations (điều này mới thực sự đối chiếu số dư)
| allocation_id | doc_type_from | doc_id_from | doc_type_to | doc_id_to | posted_at | amount |
|---|---|---|---|---|---|---|
| AL-900 | payment | PAY-77 | invoice | INV-10 | 2026-01-10 | 70.00 |
| AL-901 | credit | CR-5 | invoice | INV-10 | 2026-01-12 | 20.00 |
Cách tính số dư hóa đơn
Với INV-10, một kiểm toán viên có thể tính lại số dư mở từ các hàng nguồn:
open_balance = invoice.total + sum(adjustments) - sum(allocations)
Vậy: 120.00 + 5.00 - (70.00 + 20.00) = 35.00 còn phải trả.
Để truy vết con số “35.00”:
- Bắt đầu từ tổng hóa đơn (INV-10)
- Cộng các adjustment đã post liên kết tới cùng hóa đơn (ADJ-3)
- Trừ từng allocation đã post áp cho hóa đơn (AL-900, AL-901)
- Xác nhận mỗi allocation trỏ tới tài liệu nguồn thực tế (PAY-77, CR-5)
- Kiểm tra ngày và
posted_atđể giải thích dòng thời gian
Những sai lầm phổ biến làm vỡ đối chiếu
Hầu hết vấn đề đối chiếu không phải là “lỗi toán học.” Chúng là do thiếu quy tắc, nên cùng một sự kiện thực tế được ghi hai cách khác nhau tùy ai xử lý.
Bẫy phổ biến là dùng các hàng âm như lối tắt. Một dòng âm trên hóa đơn, một payment âm, và một dòng thuế âm có thể mang nghĩa khác nhau. Nếu cho phép số âm, hãy định nghĩa một chính sách reversal rõ ràng (ví dụ: chỉ dùng hàng reversal tham chiếu về hàng gốc, và không trộn semantics reversal với discount).
Một nguyên nhân thường gặp khác là thay đổi lịch sử. Nếu hóa đơn đã phát hành, đừng sửa nó sau để khớp với giá mới hoặc địa chỉ đã sửa. Giữ tài liệu gốc và post một adjustment hoặc credit mô tả thay đổi.
Các mẫu thường phá tổng:
- Dùng hàng âm mà không có quy tắc reversal nghiêm ngặt và tham chiếu về hàng gốc
- Chỉnh sửa hóa đơn cũ sau khi phát hành thay vì post adjustment hoặc credit note
- Trộn ID giao dịch gateway với ID nội bộ mà không có bảng mapping và nguồn sự thật rõ ràng
- Để code ứng dụng tính toán tổng trong khi các hàng hỗ trợ (thuế, phí, làm tròn, allocations) bị thiếu
- Không tách “tiền đã chuyển” (cash movement) khỏi “tiền được phân bổ” (payment áp cho hóa đơn)
Điểm cuối cùng gây nhầm lẫn nhất. Ví dụ: khách hàng trả $100, bạn áp $60 cho Invoice A và $40 cho Invoice B. Payment là một chuyển động tiền, nhưng tạo hai allocations. Nếu bạn chỉ lưu “payment = invoice”, bạn không hỗ trợ thanh toán một phần, trả thừa, hoặc tái phân bổ.
Checklist và bước tiếp theo
Trước khi thêm tính năng, đảm bảo những điều cơ bản đứng vững. Một mô hình sổ cái thanh toán đối chiếu khi mọi tổng có thể truy vết tới các hàng cụ thể, và mọi thay đổi có lịch sử audit.
Kiểm tra nhanh đối chiếu
Chạy các kiểm tra này trên một mẫu nhỏ (một khách hàng, một tháng) rồi trên toàn bộ dữ liệu:
- Mọi số đã post trên báo cáo truy ngược được tới hàng nguồn (invoice line, payment, credit memo, adjustment) với ngày posting và tiền tệ.
- Allocations không bao giờ vượt quá tài liệu áp dụng (tổng allocations cho một payment ≤ tổng payment; tương tự cho credits).
- Không có gì bị xoá. Mục sai được reverse với lý do, rồi sửa bằng một hàng mới đã post.
- Số dư mở có thể suy ra, không lưu trực tiếp (open amount = invoice total - posted allocations và credits).
- Tổng tài liệu khớp với các dòng (tổng header = tổng các dòng, thuế, phí sau quy tắc làm tròn của bạn).
Bước tiếp theo để ra sản phẩm dùng được
Khi schema vững, xây quy trình vận hành xung quanh nó:
- Giao diện admin để tạo, post và reverse invoices, payments, credits và adjustments với ghi chú bắt buộc
- Một view đối chiếu hiển thị tài liệu và allocations cạnh nhau, bao gồm ai post gì và khi nào
- Export theo định dạng tài chính cần (theo posting date, theo khách hàng, theo mapping GL nếu có)
- Quy trình đóng kỳ: khoá posting date cho các tháng đã đóng, và yêu cầu entries reversal cho sửa muộn
- Các kịch bản test (refunds, partial payments, write-offs) phải cho kết quả đúng
Nếu bạn muốn đường tắt để có portal tài chính nội bộ hoạt động nhanh hơn, AppMaster (appmaster.io) có thể giúp bạn mô hình hóa schema PostgreSQL, sinh API và xây giao diện admin từ cùng một nguồn, để quy tắc posting và allocation nhất quán khi ứng dụng phát triển.
Câu hỏi thường gặp
Khả năng đối chiếu có nghĩa là mọi tổng số xuất hiện trên báo cáo có thể được xây dựng lại từ các bản ghi nguồn và truy vết lại tới các mục ghi có ngày tháng. Nếu báo cáo nói bạn thu được $12,430, bạn phải chỉ ra được các khoản thanh toán và hoàn tiền đã được ghi có ngày tháng và cộng lại đúng con số đó, mà không dựa vào các trường bị ghi đè.
Nguyên nhân phổ biến nhất là lưu trữ các “kết quả” thay đổi như paid_amount hoặc balance_due như thể đó là sự thật. Nếu những trường đó bị cập nhật do retry, lỗi, hoặc chỉnh sửa thủ công, bạn sẽ mất lịch sử và các tổng số sẽ không còn phản ánh điều thực sự đã xảy ra.
Bởi vì mỗi loại biểu mẫu đại diện cho một sự kiện thực tế khác nhau với ý nghĩa kế toán khác nhau. Khi ép tất cả vào một bản ghi “transaction” với nhiều trường tuỳ chọn, báo cáo sẽ trở thành việc phỏng đoán và kiểm toán biến thành tranh luận về một hàng dữ liệu thực sự có ý nghĩa gì.
Credit memo làm giảm khoản khách hàng còn nợ, nhưng nó không nhất thiết là tiền được trả lại. Refund là tiền mặt chảy ra khỏi công ty. Thường thì chúng xảy ra cùng lúc nhưng về bản chất không giống nhau. Đối xử chúng như cùng một thứ (hoặc như negative payments) sẽ làm khó đối chiếu tiền mặt và đối soát với sao kê ngân hàng.
Thay vì sửa hoặc xóa, hãy ghi một reversal. Tạo các mục mới có cùng số tiền nhưng dấu ngược lại, liên kết chúng với bản ghi gốc, rồi ghi allocation sửa lại—kết quả là lịch sử thể hiện rõ đã thay đổi gì và vì sao.
Dùng các bản ghi allocation (hay applications) rõ ràng kết nối một payment hoặc credit tới một hoặc nhiều hóa đơn với allocated_amount và posted_at. Số dư mở của hóa đơn phải tính được từ tổng hóa đơn cộng điều chỉnh trừ allocations đã post.
Lưu cả ngày tài liệu và ngày ghi sổ. Ngày tài liệu là ngày khách hàng thấy, còn posting date quyết định khi nào nó xuất hiện trong báo cáo tài chính và đóng kỳ, vì vậy số liệu cuối tháng không thay đổi chỉ vì ai đó sửa hồ sơ sau đó.
Lưu chi tiết thuế và phí ở cấp dòng, kèm theo tổng chính xác bạn đã hiển thị cho khách hàng. Nếu bạn chỉ giữ tax_total ở cấp hóa đơn, rồi sẽ có trường hợp không thể giải thích hoặc tái tạo, đặc biệt khi có nhiều mức thuế hoặc ngoại lệ.
Lưu số tiền theo tiền tệ giao dịch và cũng lưu số tiền báo cáo đã quy đổi theo tỉ giá (fx_rate) dùng khi ghi sổ. Chọn một quy tắc làm tròn (theo dòng hoặc theo hóa đơn) và ghi lại mọi sai số làm tròn rõ ràng để có thể tái tạo tổng chính xác.
Dùng trạng thái như nhãn quy trình (Draft, Issued, Void, Paid), còn các mục ghi sổ bất biến và allocations mới là nguồn sự thật kế toán. Trạng thái có thể sai; các mục đã post bất biến cho phép finance tính lại cùng một cách mỗi lần.


