Module: Invoicing::CurrencyValue
- Defined in:
- lib/invoicing/currency_value.rb
Overview
Input and output of monetary values
This module simplifies model objects which need to store monetary values. It automatically takes care of currency rounding conventions and formatting values for output.
General notes on currency precision and rounding
It is important to deal carefully with rounding errors in accounts. If the figures don’t add up exactly, you may have to pay for expensive accountant hours while they try to find out where the missing pennies or cents have gone – better to avoid this trouble from the start. Because of this, it is strongly recommended that you use fixed-point or decimal datatypes to store any sort of currency amounts, never floating-point numbers.
Keep in mind that not all currencies subdivide their main unit into 100 smaller units; storing four digits after the decimal point should be enough to allow you to expand into other currencies in future. Also leave enough headroom in case you ever need to use an inflated currency. For example, if you are using MySQL, decimal(20,4)
may be a good choice for all your columns which store monetary amounts. The extra few bytes aren’t going to cost you anything.
On the other hand, it doesn’t usually make sense to store monetary values with a higher precision than is conventional for a particular currency (usually this is related to the value of the smallest coin in circulation, but conventions may differ). For example, if your currency rounds to two decimal places, then you should also round every monetary amount to two decimal places before storing it. If you store values at a higher precision than you display, your numbers may appear to not add up correctly when you present them to users. Fortunately, this module automatically performs currency-specific rounding for you.
Using acts_as_currency_value
This module simplifies model objects which need to store monetary values, by automatically taking care of currency rounding and formatting conventions. In a typical set-up, every model object which has one or more attributes storing monetary amounts (a price, a fee, a tax amount, a payment value, etc.) should also have a currency
column, which stores the ISO 4217 three-letter upper-case code identifying the currency. Annotate your model class with acts_as_currency_value
, passing it a list of attribute names which store monetary amounts. If you refuse to store a currency
attribute, you may instead specify a default currency by passing a :currency_code => CODE
option to acts_as_currency_value
, but this is not recommended: even if you are only using one currency now, you may well expand into other currencies later. It is not possible to have multiple different currencies in the same model object.
The CurrencyValue
module knows how to handle a set of default currencies (see CURRENCIES
below). If your currency is not supported in the way you want, you can extend/modify the hash yourself (please also send us a patch so that we can extend our list of inbuilt currencies):
Invoicing::CurrencyValue::CURRENCIES['HKD'] = {:symbol => 'HK$', :round => 0.10, :digits => 2}
This specifies that the Hong Kong Dollar should be displayed using the ‘HK$’ symbol and two digits after the decimal point, but should always be rounded to the nearest 10 cents since the 10 cent coin is the smallest in circulation (therefore the second digit after the decimal point will always be zero).
When that is done, you can use the model object normally, and rounding will occur automatically:
invoice.currency = 'HKD'
invoice.tax_amount = invoice.net_amount * TaxRates.default_rate_now # 1234.56789
invoice.tax_amount == BigDecimal('1234.6') # true - rounded to nearest 0.01
Moreover, you can just append _formatted
to your attribute name and get the value formatted for including in your views:
invoice.tax_amount_formatted # 'HK$1,234.60'
The string returned by a _formatted
method is UTF-8 encoded – remember most currency symbols (except $) are outside basic 7-bit ASCII.
Defined Under Namespace
Modules: ActMethods, Formatter Classes: ClassInfo
Constant Summary collapse
- CURRENCIES =
Data about currencies, indexed by ISO 4217 code. (Currently a very short list, in need of extending.) The values are hashes, in which the following keys are recognised:
:round
-
Smallest unit of the currency in normal use, to which values are rounded. Default is 0.01.
:symbol
-
Symbol or string usually used to denote the currency. Encoded as UTF-8. Default is ISO 4217 code.
:suffix
-
true
if the currency symbol appears after the number,false
if it appears before. Defaultfalse
.
{ 'EUR' => {:symbol => "\xE2\x82\xAC"}, # Euro 'GBP' => {:symbol => "\xC2\xA3"}, # Pound Sterling 'USD' => {:symbol => "$"}, # United States Dollar 'CAD' => {:symbol => "$"}, # Canadian Dollar 'AUD' => {:symbol => "$"}, # Australian Dollar 'CNY' => {:symbol => "\xE5\x85\x83", :suffix => true}, # Chinese Yuan (RMB) 'INR' => {:symbol => "\xE2\x82\xA8"}, # Indian Rupee 'JPY' => {:symbol => "\xC2\xA5", :round => 1} # Japanese Yen }
Instance Method Summary collapse
-
#format_currency_value(value, options = {}) ⇒ Object
Format a numeric monetary value into a human-readable string, in the currency of the current model object.
Instance Method Details
#format_currency_value(value, options = {}) ⇒ Object
Format a numeric monetary value into a human-readable string, in the currency of the current model object.
122 123 124 |
# File 'lib/invoicing/currency_value.rb', line 122 def format_currency_value(value, ={}) currency_value_class_info.format_value(self, value, ) end |