Không bao giờ dùng float/double cho tiền. Số thực nhị phân không biểu diễn chính xác 0.1, cộng dồn nhiều dòng sẽ lệch xu và lệch đối soát.
Hai cách đúng:
- Số nguyên theo đơn vị nhỏ nhất: lưu amount_minor BIGINT. VND không có phần lẻ nên 150000 = 150.000đ; USD thì 1050 = 10.50 USD. Cộng trừ nhanh, không có sai số.
- NUMERIC(p, s) của Postgres (kiểu thập phân chính xác): NUMERIC(19, 4) cho đa số hệ thống. Chậm hơn integer nhưng biểu diễn đúng và chia được.
Đa tiền tệ:
- Lưu currency CHAR(3) ngay cạnh mỗi số tiền (ISO 4217). Một cột tiền không có cột currency là bug chờ xảy ra.
- Không cộng hai số tiền khác currency. Muốn tổng hợp thì quy đổi tường minh.
- Khi giao dịch có quy đổi, lưu snapshot tỉ giá và thời điểm vào chính bản ghi giao dịch. Tỉ giá đổi mỗi ngày, tính lại theo tỉ giá hôm nay sẽ ra số khác với hoá đơn đã phát hành.
Làm tròn: chỉ làm tròn ở bước cuối cùng khi hiển thị hoặc khi chốt số phải thu, và ghi rõ quy tắc. Định dạng hiển thị để cho client làm bằng Intl.NumberFormat, backend trả về số thô kèm mã tiền tệ.