Mô hình dữ liệu định giá đa ngoại tệ cho thuế và hóa đơn
Tìm hiểu mô hình dữ liệu định giá đa ngoại tệ xử lý tỷ giá, làm tròn, thuế và hiển thị hóa đơn theo vùng mà không gây bất ngờ.

Những gì thường sai với hóa đơn đa ngoại tệ
Hóa đơn đa ngoại tệ hỏng theo những cách nhàm chán nhưng tốn kém. Trên giao diện số trông mọi con số đều ổn, rồi ai đó xuất PDF, kế toán nhập vào, và tổng không còn khớp với các dòng.
Nguyên nhân gốc rễ đơn giản: tính toán tiền không chỉ là nhân với tỷ giá. Thuế, làm tròn và thời điểm bạn khóa tỷ giá đều ảnh hưởng đến kết quả. Nếu mô hình dữ liệu giá của bạn không ghi rõ những lựa chọn đó, các phần khác nhau của hệ thống sẽ "giúp" tính lại và cho ra kết quả khác nhau.
Ba góc nhìn phải đồng ý, ngay cả khi chúng hiển thị các loại tiền khác nhau:
- Góc nhìn khách hàng: giá rõ ràng bằng tiền của khách, với tổng cộng cộng đúng.
- Góc nhìn kế toán: các số cơ sở nhất quán để báo cáo và đối chiếu.
- Góc nhìn kiểm toán: đường dẫn bằng chứng cho thấy tỷ giá và quy tắc làm tròn nào đã tạo hóa đơn.
Sự không khớp thường đến từ những quyết định nhỏ được thực hiện ở các nơi khác nhau. Nhóm này làm tròn mỗi dòng; nhóm kia chỉ làm tròn tổng. Trang này dùng tỷ giá hiện tại; trang kia dùng tỷ giá ngày hóa đơn. Một số thuế tính trước chiết khấu, số khác sau. Có thuế đã bao gồm trong giá; có thuế cộng thêm.
Một ví dụ cụ thể: bạn bán một món hàng 19.99 EUR, lập hóa đơn bằng GBP và báo cáo bằng USD. Nếu bạn chuyển đổi từng dòng rồi làm tròn 2 chữ số, bạn có thể có tổng thuế khác với cách bạn cộng trước rồi chuyển đổi một lần. Cả hai cách đều hợp lý, nhưng bạn chỉ có thể chọn một làm quy tắc.
Mục tiêu là phép tính có thể dự đoán và giá trị lưu trữ rõ ràng. Mỗi hóa đơn phải trả lời, không phải đoán: các số nào đã được nhập, loại tiền nào dùng để nhập, tỷ giá nào đã dùng (và khi nào), phần nào được làm tròn (và như thế nào), và luật thuế nào đã áp dụng. Rõ ràng đó giữ cho tổng ổn định giữa UI, PDF, xuất khẩu và kiểm toán.
Thuật ngữ quan trọng cần thống nhất trước khi thiết kế schema
Trước khi bạn vẽ bảng, hãy đảm bảo mọi người dùng cùng một ngôn ngữ. Phần lớn lỗi đa ngoại tệ không phải kỹ thuật, mà là lỗi "chúng tôi hiểu khác nhau". Một schema sạch bắt đầu bằng các định nghĩa mà sản phẩm, tài chính và kỹ thuật đều đồng ý.
Các thuật ngữ tiền tệ ảnh hưởng đến cơ sở dữ liệu của bạn
Với mỗi luồng tiền, hãy thống nhất ba loại tiền:
- Tiền giao dịch (Transactional currency): loại tiền khách hàng thấy và đồng ý (bảng giá, giỏ hàng, hiển thị hóa đơn).
- Tiền thanh toán (Settlement currency): loại tiền bạn thực sự được thanh toán (do nhà cung cấp thanh toán hoặc ngân hàng quyết toán).
- Tiền báo cáo (Reporting currency): loại tiền dùng cho dashboard và tóm tắt kế toán.
Ngoài ra xác định đơn vị nhỏ. USD có 2 (cents), JPY có 0, KWD có 3. Điều này quan trọng vì lưu "12.34" dưới dạng số dấu phẩy động sẽ bị trôi, trong khi lưu nguyên số theo đơn vị nhỏ (ví dụ 1234 cents) thì chính xác và làm cho việc làm tròn có thể dự đoán.
Thuật ngữ thuế thay đổi tổng
Thuế cần cùng mức độ thống nhất. Quyết định giá hiển thị là bao gồm thuế (giá hiển thị đã gồm thuế) hay chưa gồm thuế (thuế cộng thêm). Cũng chọn xem thuế được tính theo dòng (rồi cộng lại) hay theo hóa đơn (cộng trước rồi tính thuế). Những lựa chọn này ảnh hưởng đến làm tròn và có thể thay đổi số tiền phải trả vài đơn vị nhỏ.
Cuối cùng, quyết định những gì phải lưu và những gì có thể suy ra:
- Lưu những gì quan trọng về mặt pháp lý và tài chính: giá đã thỏa thuận, thuế đã áp dụng, tổng đã làm tròn cuối cùng, và loại tiền sử dụng.
- Suy ra những gì có thể tính lại an toàn: chuỗi đã định dạng, chuyển đổi chỉ để hiển thị, và hầu hết các phép tính trung gian.
Các trường tiền cốt lõi: lưu gì và như thế nào
Bắt đầu bằng cách quyết định số nào là thực tế bạn lưu và số nào là kết quả có thể tính lại. Trộn hai thứ này là cách khiến hóa đơn hiện màn hình khác với xuất khẩu.
Lưu tiền dưới dạng số nguyên theo đơn vị nhỏ (như cents) và luôn lưu mã tiền kèm theo. Một số tiền không kèm loại tiền là dữ liệu không hoàn chỉnh. Số nguyên cũng tránh sai số dấu phẩy động nhỏ xuất hiện khi cộng nhiều dòng.
Một mẫu thực tế là giữ cả đầu vào thô và kết quả tính toán. Đầu vào giải thích người dùng đã nhập gì. Kết quả giải thích bạn đã tính phí gì. Khi ai đó tranh chấp hóa đơn sau vài tháng, bạn cần cả hai.
Với dòng hóa đơn, một tập trường sạch và bền có thể là:
unit_price_minor+unit_currencyquantity(vàuomnếu cần)line_subtotal_minor(trước thuế/chiết khấu)line_discount_minorline_tax_minor(hoặc chia theo loại thuế)line_total_minor(số cuối cho dòng)
Làm tròn không chỉ là chi tiết giao diện. Hãy lưu phương pháp làm tròn và độ chính xác dùng cho phép tính, đặc biệt nếu bạn hỗ trợ tiền với đơn vị nhỏ khác nhau (JPY vs USD) hoặc quy tắc làm tròn tiền mặt. Một bản nhỏ "ngữ cảnh tính toán" có thể ghi calc_precision, rounding_mode, và liệu làm tròn xảy ra theo dòng hay chỉ ở tổng hóa đơn.
Giữ phần định dạng hiển thị riêng khỏi giá trị lưu. Các giá trị lưu nên là số và mã; định dạng (ký hiệu tiền tệ, dấu phân cách, định dạng số theo vùng) thuộc về tầng trình diễn. Ví dụ, lưu 12345 + EUR, và để UI quyết định hiển thị "€123.45" hay "123,45 €".
Tỷ giá: bảng, dấu thời gian và dấu vết kiểm toán
Xử lý tỷ giá như dữ liệu theo thời gian với nguồn rõ ràng. "Tỷ giá hôm nay" không phải thứ có thể an toàn tính lại sau này.
Một bảng tỷ giá thực tế thường bao gồm:
base_currency(chuyển từ, ví dụ USD)quote_currency(chuyển sang, ví dụ EUR)rate(số thập phân độ chính xác cao: đơn vị quote trên 1 base)effective_at(dấu thời gian tỷ giá có hiệu lực)source(nhà cung cấp) vàsource_ref(ID của họ hoặc hash payload)
Thông tin nguồn đó quan trọng khi kiểm toán. Nếu khách hàng tranh chấp, bạn có thể chỉ ra chính xác số đó đến từ đâu.
Tiếp theo, chọn một quy tắc về lúc nào hóa đơn dùng tỷ giá, rồi tuân thủ nó. Các tùy chọn phổ biến: tỷ giá thời điểm đặt hàng, thời điểm giao hàng, hoặc thời điểm lập hóa đơn. Lựa chọn tốt nhất phụ thuộc vào nghiệp vụ. Quan trọng là nhất quán và có tài liệu.
Dù chọn gì, lưu tỷ giá chính xác đã dùng trên hóa đơn (và thường trên từng dòng). Đừng dựa vào việc tra lại sau này. Thêm các trường như fx_rate, fx_rate_effective_at, và fx_rate_source để hóa đơn có thể tái tạo chính xác.
Với tỷ giá thiếu (cuối tuần, lễ, nhà cung cấp outage), làm rõ hành vi dự phòng. Cách thường gặp: dùng tỷ giá gần nhất trước đó, khóa việc lập hóa đơn cho tới khi có tỷ giá, hoặc cho phép tỷ giá thủ công kèm flag phê duyệt.
Ví dụ: đơn đặt hàng đặt vào thứ Bảy, giao hàng thứ Hai, lập hóa đơn thứ Hai. Nếu quy tắc của bạn là thời điểm lập hóa đơn nhưng nhà cung cấp không công bố tỷ giá cuối tuần, bạn có thể dùng tỷ giá cuối thứ Sáu và ghi effective_at = Friday 23:59, cùng source_ref để truy xuất.
Chuyển đổi tiền tệ và quy tắc làm tròn để giữ nhất quán
Vấn đề làm tròn hiếm khi trông giống lỗi rõ ràng. Nó xuất hiện dưới dạng chênh lệch 1 cent giữa tổng hóa đơn và tổng các dòng, hoặc khác biệt thuế nhỏ giữa hiển thị và yêu cầu của nhà cung cấp thanh toán. Mô hình tốt biến làm tròn thành quy tắc bạn có thể giải thích, không phải là việc vá lỗi sau này.
Quyết định chính xác nơi làm tròn xảy ra
Chọn các điểm bạn cho phép làm tròn, và giữ mọi thứ khác ở độ chính xác cao hơn. Điểm làm tròn phổ biến bao gồm:
- Mở rộng theo dòng (số lượng x đơn giá, sau chiết khấu)
- Mỗi khoản thuế (theo dòng hoặc trên tổng, tùy thẩm quyền)
- Tổng cuối cùng của hóa đơn
Nếu bạn không xác định các điểm này, các phần khác của hệ thống sẽ làm tròn khi tiện lợi, và tổng sẽ dịch dần.
Dùng một chế độ làm tròn duy nhất, với ngoại lệ rõ ràng cho luật thuế
Chọn một chế độ làm tròn (half-up hoặc bankers rounding) và áp dụng nhất quán. Half-up dễ giải thích với khách hàng. Bankers rounding giảm thiên lệch trên khối lượng lớn. Cả hai đều được, nhưng API, UI, xuất và báo cáo kế toán phải cùng chế độ.
Giữ độ chính xác thừa trong chuyển đổi và bước trung gian (ví dụ lưu tỷ giá với nhiều chữ số thập phân), rồi chỉ làm tròn ở các điểm bạn chọn.
Chiết khấu cũng cần một quy tắc duy nhất: áp dụng trước thuế (thường cho coupon) hay sau thuế (đôi khi bắt buộc cho phí cụ thể). Ghi lại và mã hóa một lần.
Một số nơi yêu cầu làm tròn thuế theo dòng, theo loại thuế, hoặc trên tổng hóa đơn. Thay vì mã hóa các trường hợp rời rạc khắp codebase, lưu một cài đặt "chính sách làm tròn" (theo quốc gia/tỉnh/chedo thue) và cho phép phép tính tuân theo chính sách đó.
Một kiểm tra đơn giản: nếu bạn dựng lại cùng một hóa đơn ngày mai với cùng tỷ giá và chính sách đã lưu, bạn phải ra đúng từng cent.
Trường thuế: mẫu cho VAT, sales tax và nhiều thuế
Thuế trở nên rối nhanh vì phụ thuộc vào nơi mua, mặt hàng bán và giá hiển thị là net hay gross. Một mô hình sạch giữ thuế rõ ràng, không ngụ ý.
Làm cho cơ sở tính thuế không mơ hồ. Lưu giá trị bạn đang tính thuế là NET (thuế cộng thêm) hay GROSS (đã gồm). Rồi lưu cả tỷ lệ bạn đã áp dụng và số thuế tính được như một snapshot, để thay đổi luật sau này không sửa lịch sử.
Trên mỗi dòng hóa đơn, một tập tối thiểu rõ ràng qua năm tháng:
tax_basis(NET hoặc GROSS)tax_rate(thập phân, ví dụ 0.20)taxable_amount_minor(cơ sở bạn thực tế tính thuế)tax_amount_minortax_method(PER_LINE hoặc ON_SUBTOTAL)
Nếu có hơn một loại thuế có thể áp dụng (ví dụ VAT cộng lệ phí thành phố), thêm một bảng phân tích như InvoiceLineTax với một hàng cho mỗi thuế áp dụng. Mỗi hàng nên bao gồm tax_code, tax_rate, taxable_amount_minor, tax_amount_minor, mã tiền và gợi ý thẩm quyền dùng khi tính (quốc gia, vùng, mã bưu chính khi liên quan).
Giữ snapshot chi tiết quy tắc đã áp dụng trên hóa đơn hoặc dòng hóa đơn, như rule_version hoặc một blob JSON chứa đầu vào quyết định (tình trạng thuế của khách hàng, reverse charge, miễn trừ). Nếu luật VAT thay đổi năm sau, các hóa đơn cũ vẫn phải khớp với những gì bạn thực tế đã thu.
Ví dụ: một thuê bao SaaS bán cho khách ở Đức có thể áp 19% VAT trên giá NET của dòng, cộng 1% thuế địa phương. Lưu tổng từng dòng như đã thu và giữ một hàng phân tích cho mỗi loại thuế để hiển thị và kiểm toán.
Thiết kế bảng từng bước
Đây ít liên quan đến toán học tinh vi hơn là cố định các sự thật đúng lúc. Mục tiêu là mở lại hóa đơn vài tháng sau vẫn hiện cùng con số.
Bắt đầu bằng cách quyết định nơi nào là nguồn chân thực cho giá sản phẩm. Nhiều nhóm giữ một giá gốc theo tiền cơ sở cho mỗi sản phẩm và tuỳ chọn có override theo thị trường (ví dụ hàng giá riêng cho USD và EUR). Dù chọn gì, làm cho điều đó rõ trong schema để bạn không trộn "giá danh mục" với "giá đã chuyển đổi".
Một chuỗi đơn giản giúp bảng dễ hiểu:
- Sản phẩm và giá:
product_id,price_amount_minor,price_currency,effective_from(nếu giá thay đổi theo thời gian). - Header đơn hàng và hóa đơn:
document_currency,customer_locale,billing_country, và các dấu thời gian (issued_at,tax_point_at). - Dòng mục:
unit_price_amount_minor,quantity,discount_amount_minor,tax_amount_minor,line_total_amount_minor, và mã tiền cho mỗi trường tiền. - Snapshot tỷ giá: tỷ giá chính xác dùng (
rate_value,rate_provider,rate_timestamp) tham chiếu từ đơn hàng hoặc hóa đơn. - Bản phân tích thuế: một hàng cho mỗi thuế (
tax_type,rate_percent,taxable_base_minor,tax_amount_minor) cùng flagcalculation_method.
Đừng tin tưởng vào việc tính lại sau này. Khi bạn tạo hóa đơn, sao chép giá đơn vị cuối cùng, chiết khấu và tổng lên dòng hóa đơn, ngay cả khi chúng đến từ đơn hàng.
Để truy xuất nguồn, thêm calculation_version (hoặc calc_hash) trên hóa đơn và một bảng calculation_log nhỏ ghi ai đã kích hoạt tính toán lại và vì lý do gì (ví dụ "cập nhật tỷ giá trước khi phát hành").
Hiển thị hóa đơn theo vùng mà không làm vỡ số
Localization nên thay đổi cách hóa đơn trông như thế nào, không phải ý nghĩa của nó. Thực hiện mọi phép tính bằng các giá trị số đã lưu (đơn vị nhỏ hoặc số thập phân cố định), rồi áp định dạng theo vùng ở bước cuối cùng.
Giữ các thiết đặt trình bày hóa đơn trên chính hóa đơn, không chỉ trên hồ sơ khách hàng. Khách hàng thay đổi quốc gia, liên hệ thanh toán và tuỳ chọn theo thời gian. Hóa đơn là snapshot pháp lý. Lưu thứ như invoice_language, invoice_locale, và cờ định dạng (ví dụ có hiển thị số 0 kết thúc hay không) cùng với tài liệu để in lại sau vài tháng khớp nguyên bản.
Ký hiệu tiền là vấn đề hiển thị. Một số vùng đặt ký hiệu trước số, số khác sau. Một số cần khoảng trắng, số khác không. Xử lý vị trí ký hiệu, khoảng cách, dấu thập phân và nhóm hàng nghìn khi render, theo invoice_locale và tiền tệ. Đừng ghi ký hiệu vào trường tiền đã lưu, và đừng parse chuỗi đã định dạng về số.
Nếu cần báo cáo bằng tiền thứ hai (thường là tiền nhà như USD hoặc EUR), hiển thị rõ ràng nó như tổng phụ, không thay thế tiền tài liệu. Tiền tài liệu vẫn là nguồn pháp lý duy nhất.
Một thiết lập thực tế cho xuất hóa đơn:
- Hiển thị dòng mục và tổng theo tiền tài liệu, dùng định dạng theo
invoice-locale. - Tuỳ chọn hiển thị tổng báo cáo phụ kèm nguồn tỷ giá và dấu thời gian.
- Hiển thị phân tích thuế dưới dạng các dòng riêng (cơ sở chịu thuế, từng khoản thuế, tổng thuế), không gộp chung.
- Render PDF và email từ cùng tổng đã lưu để số không bị trôi.
Ví dụ: khách Pháp được tính bằng CHF. Locale hóa đơn dùng dấu phẩy và đặt tiền phía sau, nhưng phép tính vẫn dùng các giá trị CHF đã lưu và tổng thuế đã lưu. Đầu ra được định dạng thay đổi; các số thì không.
Sai lầm phổ biến và bẫy cần tránh
Cách nhanh nhất phá hóa đơn đa ngoại tệ là coi tiền như số bình thường. Kiểu dấu phẩy động cho giá, thuế và tổng tạo lỗi nhỏ hiện lên sau này dưới dạng "thiếu 0.01$". Lưu số theo đơn vị nhỏ (cents) hoặc dùng kiểu thập phân cố định với scale rõ ràng, rồi dùng nhất quán.
Bẫy cổ điển khác là vô tình thay đổi lịch sử. Nếu bạn tính lại hóa đơn cũ với tỷ giá hôm nay, hoặc với luật thuế cập nhật, bạn không còn giữ văn bản khách hàng đã thấy và đã thanh toán. Hóa đơn nên bất biến: một khi phát hành, lưu tỷ giá chính xác, quy tắc làm tròn và phương pháp thuế đã dùng, và đừng tính lại các tổng đã lưu.
Trộn tiền tệ trong một dòng mục cũng là lỗi schema im lặng. Nếu đơn giá bằng EUR, chiết khấu bằng USD, và thuế tính bằng GBP, bạn không thể giải thích phép toán sau này. Chọn một tiền tài liệu để hiển thị và thanh toán, và một tiền cơ sở cho báo cáo nội bộ (nếu cần). Mỗi số lưu phải kèm mã tiền rõ ràng.
Các lỗi làm tròn thường từ việc làm tròn quá nhiều lần. Nếu bạn làm tròn tại đơn giá, rồi tổng dòng, rồi thuế từng dòng, rồi subtotal lại, tổng có thể không còn bằng tổng các dòng.
Những bẫy phổ biến:
- Dùng float cho tiền hoặc tỷ giá mà không có độ chính xác cố định
- Tính lại các hóa đơn cũ thay vì dùng tỷ giá đã lưu
- Cho phép một dòng chứa các số ở nhiều tiền tệ khác nhau
- Làm tròn ở quá nhiều bước thay vì ở các điểm xác định
- Không lưu dấu thời gian tỷ giá, chế độ làm tròn và phương pháp thuế theo mỗi tài liệu
Ví dụ: bạn tạo hóa đơn bằng CAD, chuyển đổi một dịch vụ có giá EUR, rồi sau này cập nhật bảng tỷ giá. Nếu bạn chỉ lưu số EUR rồi chuyển đổi khi hiển thị, tổng CAD thay đổi tuần sau. Hãy lưu số EUR, tỷ giá đã áp (và thời điểm), và các số CAD cuối cùng dùng trên hóa đơn.
Checklist nhanh trước khi phát hành
Trước khi bạn gọi hóa đơn đa ngoại tệ là "xong", rà soát lần cuối tập trung vào tính nhất quán. Hầu hết lỗi ở đây không phức tạp. Chúng đến từ sự không khớp giữa những gì bạn lưu, những gì bạn hiển thị và những gì bạn cộng.
Dùng đây làm cổng phát hành:
- Mỗi hóa đơn có đúng một
document_currencyở header, và mọi tổng bạn lưu trên hóa đơn đều ở tiền đó. - Mọi giá trị tiền bạn lưu là số nguyên đơn vị nhỏ, bao gồm tổng dòng, thuế, chiết khấu và phí vận chuyển.
- Hóa đơn lưu tỷ giá chính xác đã dùng (dưới dạng số thập phân chính xác), cùng dấu thời gian và nguồn tỷ giá.
- Quy tắc làm tròn được ghi lại và triển khai ở một nơi chia sẻ.
- Nếu có hơn một thuế, bạn lưu phân tích thuế theo dòng (và tuỳ chọn theo thẩm quyền), không chỉ tổng thuế trên header.
Sau khi schema ổn, xác nhận phép tính theo cách kiểm toán viên sẽ làm. Tổng hóa đơn phải bằng tổng các line_total đã lưu và các tax_amount đã lưu. Đừng tính lại tổng từ các giá trị đã định dạng hay chuỗi hiển thị.
Một test thực tế: chọn một hóa đơn có ít nhất ba dòng, áp chiết khấu, và có hai loại thuế trên một dòng. Sau đó in ở locale khác (dấu phân cách và ký hiệu tiền khác) và xác nhận các số đã lưu không thay đổi.
Ví dụ tình huống: một đơn, ba loại tiền và thuế
Một khách Mỹ được lập hóa đơn bằng USD, nhà cung cấp EU tính bạn bằng EUR, và team tài chính báo cáo bằng GBP. Đây là nơi mô hình hoặc ổn định hoặc thành mớ lỗi 1-cent.
Đơn: 3 đơn vị của một sản phẩm.
- Giá khách: $19.99 mỗi đơn vị (USD)
- Chiết khấu: 10% cho dòng
- Thuế bán hàng Mỹ: 8.25% (tính sau chiết khấu)
- Chi phí nhà cung cấp: EUR 12.40 mỗi đơn vị (EUR)
- Tiền báo cáo: GBP
Quy trình: chuyện gì xảy ra và khi nào bạn chuyển đổi
Chọn một thời điểm chuyển đổi và tuân thủ nó. Trong nhiều hệ thống, an toàn là chuyển đổi tại thời điểm phát hành hóa đơn, rồi lưu tỷ giá chính xác đã dùng.
Khi tạo hóa đơn:
- Tính subtotal dòng USD: 3 x 19.99 = 59.97 USD.
- Áp chiết khấu: 59.97 x 10% = 5.997, làm tròn thành 6.00 USD.
- Dòng ròng: 59.97 - 6.00 = 53.97 USD.
- Thuế: 53.97 x 8.25% = 4.452525, làm tròn thành 4.45 USD.
- Tổng: 53.97 + 4.45 = 58.42 USD.
Làm tròn chỉ xảy ra tại các điểm đã định (chiết khấu, mỗi khoản thuế, tổng dòng). Lưu các kết quả đã làm tròn đó, và luôn cộng các giá trị đã lưu. Điều này ngăn tình huống cổ điển nơi PDF hiển thị 58.42 nhưng xuất khẩu tính lại ra 58.43.
Những gì bạn lưu để có thể tái tạo hóa đơn sau này
Trên hóa đơn (và dòng hóa đơn), lưu mã tiền (USD), các số theo đơn vị nhỏ (cents), phân tích thuế theo từng loại, và ID bản ghi tỷ giá đã dùng để chuyển USD sang GBP cho báo cáo. Với chi phí nhà cung cấp, lưu chi phí EUR và bản ghi tỷ giá nếu bạn cũng chuyển chi phí sang GBP.
Khách hàng thấy hóa đơn USD rõ ràng (giá, chiết khấu, thuế, tổng). Finance xuất khẩu số USD cùng các tương đương GBP đã đóng băng và dấu thời gian tỷ giá chính xác, nên số cuối tháng khớp ngay cả khi tỷ giá thay đổi ngày mai.
Bước tiếp theo: triển khai, test và giữ cho dễ bảo trì
Ghi lại schema tối thiểu của bạn như một hợp đồng ngắn: những số nào được lưu (gốc, đã chuyển đổi, thuế), tiền của từng số, quy tắc làm tròn áp dụng, và dấu thời gian khóa tỷ giá cho hóa đơn. Giữ nó nhàm chán và cụ thể.
Trước khi xây UI, viết test. Đừng chỉ test hóa đơn bình thường. Thêm các trường hợp biên đủ nhỏ để lộ nhiễu làm tròn và đủ lớn để lộ vấn đề tổng hợp.
Một bộ test khởi động:
- Giá đơn vị rất nhỏ (ví dụ 0.01) nhân với số lượng lớn
- Chiết khấu tạo ra thập phân lặp sau khi chuyển đổi
- Thay đổi tỷ giá giữa ngày đặt hàng và ngày lập hóa đơn
- Quy tắc thuế hỗn hợp (đã gồm thuế vs chưa gồm thuế) trên cùng kiểu hóa đơn
- Hoàn tiền và ghi nợ phải khớp với làm tròn ban đầu
Để rút ngắn ticket hỗ trợ, thêm một view audit giải thích mọi số trên hóa đơn: các giá trị đã lưu, mã tiền, ID và dấu thời gian tỷ giá, và phương pháp làm tròn sử dụng. Khi ai đó hỏi "tại sao tổng khác?", bạn có thể trả lời từ các sự thật đã lưu.
Nếu bạn đang xây công cụ billing nội bộ, nền tảng no-code như AppMaster (appmaster.io) có thể giúp giữ nhất quán bằng cách đặt schema ở một nơi và logic tính toán trong một workflow tái sử dụng, để web và mobile không mỗi nơi tự làm toán của mình.
Cuối cùng, phân công trách nhiệm. Quyết định ai cập nhật tỷ giá, ai cập nhật luật thuế, và ai phê duyệt thay đổi ảnh hưởng đến hóa đơn đã phát hành. Ổn định là một quy trình, không chỉ một schema.


