Module: Invoicing::CurrencyValue

Extended by:
ActiveSupport::Concern
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. Default false.

{
  '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

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.


127
128
129
# File 'lib/invoicing/currency_value.rb', line 127

def format_currency_value(value, options={})
  currency_value_class_info.format_value(self, value, options)
end