Exchange Rates Plugin
DescriptionThe exchange-rates plugin will either retrieve foreign currency exchange rates dynamically from Yahoo! or can store rates in the database on a day-by-day, currency-by-currency basis retrieving the rate from Yahoo! the first time each day that a conversion is requested for a given currency. A full set of CRUD screens is included together with a cache statistics screen (at a URL such as http://myServer/myApp/exchangeRate/cache) and a test screen for checking that the system is working (at a URL such as http://myServer/myApp/exchangeRate/test). The screens assume you are using a layout called main. Your application must have Internet access for the exchange-rates plugin to function.
InstallationExecute the following from your application directory:
The plugin creates two domains called ExchangeCurrency and ExchangeRate. It also copies a properties file called exchange-rates.properties to the i18n directory of your application overwriting any file of the same name. Two image files (false.png and true.png) are copied to the images directory of your application overwriting any files of the same name. After installation, the ExchangeCurrency table in your database should have a unique index on the 'code' column, but since Hibernate may or may not create this index, you are advised to check it exists otherwise performance may suffer.Before using the plugin you need to determine what your base currency will be. Daily (as opposed to dynamic) rates are computed via a base currency whose exchange rate is always 1.0. The base currency has no other significance than this. Once the plugin is used, you cannot change the base currency without creating inconsistent data in the database. If you do not specify otherwise, the base currency will be USD (US Dollars). If you wished the base currency to be European Euros (ISO 4217 code EUR), for example, then you would make an entry in Config.groovy as follows:
grails install-plugin exchange-rates
By default, the exchange-rates plugin allows manual entry of exchange rates for the future to accomodate companies who set fixed 'corporate' exchange rates on a monthly, quarterly or annual basis etc. If you do not wish to allow the manual entry of future rates, make the following entry in your Config.groovy:
Daily exchange rates are held in memory in a 'least recently used cache' for fast repeated access. The default maximum cache size is 8kb, but memory is only used as is needed. If you wish to alter the maximum size (amount of memory) used by the cache, you may do so by making an entry similar to the following in your Config.groovy file:
The above example Config.groovy entry increases the cache size to 12kb. Setting the cache size to zero disables caching with a consequent increase in databases activity. You can check the cache statistics using a URL such as: http://myServer/myApp/exchangeRate/cache. Note that you may have to refresh your browser window to see the most up to date statistics.If your server needs a proxy to access the Internet, then according to the Groovy simple file download from URL example, you may globally make the proxy settings as shown in that example.
UsageThe components of the plugin are in a package called org.grails.plugins.exchangerates and any class that wishes to access the components directly must include the following:
Note that to use the programmatic interface of the exchange-rates plugin, you need access to the ExchangeRateService by including the following statement in a controller or service of your application:
Dynamic Exchange RatesDynamic rates are only available programatically since the required rate is obtained dynamically from Yahoo! each time you ask for it. There are no screens within the plugin that relate to dynamic rates other than the test screen as described in the Testing section below. To obtain the current Yahoo! exchange rate from, say, Canadian Dollars to Australian Dollars, you would include the following code in your controller or service:
The above code would leave the 'rate' variable (a BigDecimal with up to 6 digits after the decimal point) holding either the exchange rate from Canadian Dollars to Australian Dollars or null if no such rate could be obtained from Yahoo! The following example shows a complete conversion of a value from Canadian Dollars to Australian Dollars including rounding the result to the correct number of decimal places for Australian Dollars:
def rate = exchangeRateService.dynamicRate("CAD", "AUD")
Since the above example is such a typical usage scenario, you could have achieved the same result more simply (and including error checking) as follows:
def canadianValue = 1.23 def rate = exchangeRateService.dynamicRate("CAD", "AUD") def decs = exchangeRateService.decimalPlacesFor("AUD") def rawValue = canadianValue * rate def australianValue = rawValue.setScale(decs, java.math.RoundingMode.HALF_UP)
In the above example, the 'australianValue' variable would either contain the converted value (rounded to the correct number of decimal places for Australian Dollars) or null if the conversion could not be performed. If you were to perform the above example twice with a one second interval between the method calls, you would quite possibly get two different results since this is the essence of dynamic exchange rates. Similarly, dynamic exchange rates do not necessarily offer mathematic symmetry since exchange rates are based on actual human trading. For example, even if you obtained multiple conversion rates at the same instant in time, there is no guarantee that converting a value from, say, Canadian Dollars to Australian Dollars to New Zealand Dollars then back to Canadian Dollars would give you the value you first started with - even allowing for mathematical rounding errors. Daily rates, as described below, offer a more stable situation at the expense of accuracy at a point in time.
def australianValue = exchangeRateService.convertDynamic(1.23, "CAD", "AUD")
Daily Exchange RatesFor currencies flagged as auto-updatable, the first time each day that a currency is involved in a currency conversion (irrespective of the date for which the conversion is being requested) the current day's exchange rate for that currency is retrieved from Yahoo! and stored in the database if no manual rate has been entered for the current date. For currencies not flagged as auto-updatable you must manually enter the exchange rates yourself. The exchange rates stored in the database are relative to the base currency (which always has a rate of 1.0). For example, if the base currency is US Dollars and one US Dollar would buy you 0.75 European Euros, then the rate for European Euros would be 0.75 in the database. Consequently, the formula for converting from one currency to another is: toValue = fromValue * (toRate / fromRate). Subject to rounding limitations, this gives a mathematical symmetry to the operation of the system (i.e. converting one currency to another followed, later in the day, by the conversion back to the original currency should give the original value) and historical consistency - assuming no manual changes are made to the rates of the currencies involved.Note that in all discussions about daily exchange rates, dates are relative to the server. This means that if you have users in different time zones from the server you may wish to adjust the date from the user's time zone to the server's time zone using Java's getRawOffset() method of the TimeZone class for both the server and user's time zones. Be aware, however, that if you do this you may end up with a date which, from the server's point of view, is 'tomorrow' and for which no rates may be available.The exchange-rates plugin includes CRUD screens for maintaining currencies and their daily exchange rates. Of the currencies defined automatically for you on first use of the plugin, you will see that some are flagged as auto-updatable and others are not. This is because Yahoo! does not offer exchange rates for all possible currencies. Some companies set fixed exchange rates per month, quarter or year. In such cases you should consider setting all currencies to non-auto-updatable.To obtain the daily exchange rate from, say, Canadian Dollars to Australian Dollars as on a given date, you would include the following code in your controller or service:
The above code would leave the 'rate' variable (a BigDecimal with up to 6 digits after the decimal point) holding either the exchange rate from Canadian Dollars to Australian Dollars on the given date or null if no such rate could be obtained from the database. If you omit the 'date' parameter, 'today' will be assumed. If you would like the plugin to perform a full conversion for you, including rounding the resulting value to the correct number of decimal places for the target currency, you could use code similar to the following:
def rate = exchangeRateService.dailyRate("CAD", "AUD", date)
Again, if you omit the date parameter 'today' will be assumed. The 'australianValue' variable will either contain the Australian Dollar (AUD) equivalent of 1.23 Canadian Dollars (CAD) or null if the conversion cannot be performed - as when rates are unavailable from the database...The opening paragraph of this section describes how daily exchange rates for 'today' are automatically inserted in to the database whenever a currency is involved for the first time that day in a daily currency conversion (irrespective of the date for which the conversion is to be performed). This implies that, if a specific currency is not used in a conversion on any given day, its rate for that day will not be updated in the database. This implication is indeed correct. If, for example, a specific currency has rates in the database for 2008-12-01 and 2008-12-03 but you subsequently ask the plugin for the historic rate on 2008-12-02, you will be given the latest rate on or before 2008-12-02. In this example you would therfore get the rate for 2008-12-01. If this is not what you want, then you can programatically perform a bulk update of the rates (presumably each day) of all auto-updatabale currencies using the following code:
def australianValue = exchangeRateService.convertDaily(1.23, "CAD", "AUD", date)
Finally, there are two additional methods available in the ExchangeRateService that you might wish to use. Firstly, if you wish to know the base currency code of your system, you can ask the plugin as follows:
By default, the variable 'base' would contain 'USD' for US Dollars unless you have changed the base currency as described in the Installation section above. Secondly, you might wish to know if manual entry of future exchange rates is permitted. You can do this as follows:
def base = exchangeRateService.baseCurrencyCode()
The 'allowed' variable in the above example will contain either of the boolean values true or false as applicable. Manual entry of future exchange rates is permitted by default but can be changed as described in the Installation section above.
def allowed = exchangeRateService.futureRatesAllowed()