Skip to content

Commit

Permalink
Item14482: add support for currency conversion
Browse files Browse the repository at this point in the history
... using online exchange rate services
  • Loading branch information
MichaelDaum committed Sep 18, 2017
1 parent f3fe101 commit 3efa50d
Show file tree
Hide file tree
Showing 7 changed files with 358 additions and 34 deletions.
64 changes: 49 additions & 15 deletions data/System/NumberPlugin.txt
@@ -1,4 +1,4 @@
%META:TOPICINFO{author="ProjectContributor" comment="reprev" date="1483464660" format="1.1" reprev="1" version="1"}%
%META:TOPICINFO{author="ProjectContributor" comment="" date="1505722717" format="1.1" version="1"}%
---+!! %TOPIC%
%FORMFIELD{"Description"}%

Expand All @@ -12,26 +12,38 @@ This plugin allows to format

in a localized way using the [[http://cldr.unicode.org/][Unicode CLDR]].

It also allows to convert currencies using an online exchange rates service. Supported services are:

* [[http://fixer.io/][Fixer.IO]]
* [[https://openexchangerates.org/][Open Exchange Rates]]

Both of these services require an API key/id to be downloaded by registering for an appropriate service level. Up-to-date exchange rates will then be
downloaded and cached for a specific time, e.g. refreshing rates once a day.

---++ Usage
For example, a number such as =1234= is formatted differently depending on the locales

| *Number* | *Type* | *Locale* | *Parameter* | *Output* |
| =1234.00= | number | =en_US= | | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"1234.00\" locale=\"en_US\"}$percnt" else="1,234"}% |
| | | =de_DE= | | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"1234.00\" locale=\"de_DE\"}$percnt" else="1.234"}% |
| | | =ar= | | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"1234.00\" locale=\"ar\"}$percnt" else="١٬٢٣٤"}% |
| | | =es= | | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"1234.00\" locale=\"es\"}$percnt" else="1234"}% |
| =1234.00= | currency | =en_US= | currency_code="USD" | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"1234.00\" locale=\"en_US\" type=\"currency\" currency_code=\"USD\"}$percnt" else="$1,234.00"}% |
| | currency | =de_DE= | currency_code="EUR" | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"1234.00\" locale=\"de_DE\" type=\"currency\" currency_code=\"EUR\"}$percnt" else="1.234,00 €"}% |
| | currency | =ar= | currency_code="AED" | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"1234.00\" locale=\"ar\" type=\"currency\" currency_code=\"AED\"}$percnt" else="د.إ.‏ ١٬٢٣٤٫٠٠"}% |
| | currency | =ar= | currency_code="USD" | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"1234.00\" locale=\"ar\" type=\"currency\" currency_code=\"USD\"}$percnt" else="US$ ١٬٢٣٤٫٠٠"}% |
| | currency | =es= | currency_code="EUR" | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"1234.00\" locale=\"es\" type=\"currency\" currency_code=\"EUR\"}$percnt" else="1234,00 €"}% |
| =1234.00= | currency | =en_US= | currency="USD" | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"1234.00\" locale=\"en_US\" type=\"currency\" currency=\"USD\"}$percnt" else="$1,234.00"}% |
| | currency | =de_DE= | currency="EUR" | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"1234.00\" locale=\"de_DE\" type=\"currency\" currency=\"EUR\"}$percnt" else="1.234,00 €"}% |
| | currency | =ar= | currency="AED" | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"1234.00\" locale=\"ar\" type=\"currency\" currency=\"AED\"}$percnt" else="د.إ.‏ ١٬٢٣٤٫٠٠"}% |
| | currency | =ar= | currency="USD" | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"1234.00\" locale=\"ar\" type=\"currency\" currency=\"USD\"}$percnt" else="US$ ١٬٢٣٤٫٠٠"}% |
| | currency | =es= | currency="EUR" | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"1234.00\" locale=\"es\" type=\"currency\" currency=\"EUR\"}$percnt" else="1234,00 €"}% |
| =0.1234= | percent | =en_US= | | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"0.1234\" locale=\"en_US\" type=\"percent\"}$percnt" else="12%"}% |
| | | =de_DE= | | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"0.1234\" locale=\"de_DE\" type=\"percent\"}$percnt" else="12 %"}% |
| | | =ar= | | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"0.1234\" locale=\"ar\" type=\"percent\"}$percnt" else="١٢٪"}% |
| | | =es= | | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"0.1234\" locale=\"es\" type=\"percent\"}$percnt" else="12 %"}% |
| | | =en_US= | minimum_fraction_digits="1" | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"0.1234\" locale=\"en_US\" type=\"percent\" minimum_fraction_digits=\"1\" }$percnt" else="12.3%"}% |
| =12.34= | currency | =de_DE= | currency="EUR" from="USD" | %IF{"context NumberPluginEnabled" then="$percntNUMBER{\"12.34\" type=\"currency\" currency=\"EUR\" from=\"USD\"}$percnt" else="10,32 €"}% |

---++ Syntax

---+++ NUMBER

By default, numbers are formatted by the =%<nop>NUMBER{"value" param="..."}%= macro

| *Parameter* | *Description* | *Default* |
Expand All @@ -52,9 +64,10 @@ By default, numbers are formatted by the =%<nop>NUMBER{"value" param="..."}%= ma
| =minimum_grouping_digits= | | |
| =rounding_increment= | | |
| <b>Parameters for currency:</b> |||
| =currency_code= | this parameter is required when the =type= is set to =currency=; possible values are any 3-letter digits according to [[https://en.wikipedia.org/wiki/ISO_4217#Active_codes][ISO 4217]], such as =EUR= or =USD= | |
| =currency= / =currency_code= / =to= | this parameter is required when the =type= is set to =currency=; possible values are any 3-letter digits according to [[https://en.wikipedia.org/wiki/ISO_4217#Active_codes][ISO 4217]], such as =EUR= or =USD= | |
| =currency_sign= | the sign that is used to identify the currency | see current locale |
| =cash= | for boolean switch to enable rendering of cache which may be formatted differently for some locales | 0 |
| =cash= | for boolean switch to enable rendering of "cash money" which may be formatted differently for some locales | off |
| =from= | specify the source currency which is converted to the target currency as specified in the =currency= parameter using an online exchange rates service | same as source currency |
| <b>Parameters for percent:</b> |||
| =permil= | | 0 |
| =percent_sign= | | % |
Expand All @@ -72,22 +85,42 @@ $Foswiki::cfg{NumberPlugin}{Currencies} = "EUR, USD";
will create the macros =%<nop>EUR{...}%= and =%<nop>USD{...}%= as a shortcut for

<verbatim class="tml">
%NUMBER{"..." type="currency" currency_code="<code>"}%
%NUMBER{"..." type="currency" currency="<code>"}%
</verbatim>

---+++ CURRENCIES

This macro lists all available currencies as well as their exchange rate using an online exchange rates service.

| *Parameter* | *Description* | *Default* |
| ="..."= / =total= | the total amount that the rates are computed for | 1 |
| =base= | base currency to with all rates are calculated | base rate of the currency provider |
| =header= | format string preceeding the output list | |
| =footer= | format string appended to the output list | |
| =separator= | list separator | =, = |
| =format= | format string used for every item in the list of available currencies | =$code= |
| =include= | regular expression that a currency code must match to be included in the output | |
| =exclude= | regular expression that a currency code must _not_ match to be included in the output | |

The =format= parameter accepts a couple of variables that are expanded accordingly:

* =$code= the international currency code (USD, EUR, ...)
* =$rate= exchange rate vs the base currency
* =$total= total value of the currency converted to the current currency in the loop

---++ Formfield types

This plugin defines three additional formfield types that may be used defining [[%SYSTEMWEB%.DataForms][DataForms]].

* =currency=
* =number=
* =percent=
* currency
* number
* percent

Parameters are specified in the =Values= column of the formfield, i.e. =currency= _requires_ a =currency_code= parameter such as in:
Parameters are specified in the =Values= column of the formfield, i.e. the formfield type "currency" _requires_ a =currency="..."= parameter such as in:

<verbatim>
| *Name* | *Type* | *Size* | *Values* | *Description* | *Attributes* | *Default* |
| Advance Payments | currency | 30 | currency_code="eur" | | | 0 |
| Advance Payments | currency | 30 | currency="EUR" | | | 0 |
</verbatim>

---++ Installation Instructions
Expand All @@ -99,7 +132,8 @@ Parameters are specified in the =Values= column of the formfield, i.e. =currency
---++ Change History

%TABLE{columnwidths="7em" tablewidth="100%"}%
| 03 Jan 2017 | initial release |
| 18 Sep 2017 | added support for exchange rates; made compatible with <nop>SpreadsheetPlugin |
| 03 Jan 2017 | initial release |

%META:FORM{name="PackageForm"}%
%META:FIELD{name="Author" title="Author" value="Michael Daum"}%
Expand Down
2 changes: 1 addition & 1 deletion lib/Foswiki/Form/Number.pm
Expand Up @@ -74,7 +74,7 @@ sub getDisplayValue {
$result =~ s/^\s+|\s+$//g;
};

return $result;
return "<span class='foswikiNumber foswikiHidden'>$value</span>".$result;
}

sub renderForDisplay {
Expand Down
34 changes: 23 additions & 11 deletions lib/Foswiki/Plugins/NumberPlugin.pm
Expand Up @@ -20,26 +20,38 @@ use warnings;

use Foswiki::Func ();

our $VERSION = '1.00';
our $RELEASE = '03 Jan 2017';
our $SHORTDESCRIPTION = 'Localized Number Formatter';
our $VERSION = '2.00';
our $RELEASE = '18 Sep 2017';
our $SHORTDESCRIPTION = 'Localized Number Formatter and Currency Converter';
our $NO_PREFS_IN_TOPIC = 1;
our $core;

sub initPlugin {

Foswiki::Func::registerTagHandler('NUMBER', sub { return getCore()->handleNUMBER(@_); });
Foswiki::Func::registerTagHandler('CURRENCIES', sub { return getCore()->handleCURRENCIES(@_); });

if ($Foswiki::cfg{NumberPlugin}{Currencies}) {
foreach my $code (split(/\s*,\s*/, $Foswiki::cfg{NumberPlugin}{Currencies})) {
Foswiki::Func::registerTagHandler($code, sub {
my ($session, $params) = @_;
$params->{currency_code} = $code;
return getCore()->handleCurrency($params);
});
}
foreach my $code (getCore()->getCurrencies) {
next if $code eq "TOP"; # breaks some wiki apps
Foswiki::Func::registerTagHandler($code, sub {
my ($session, $params) = @_;
$params->{currency_code} = $code;
return getCore()->handleCurrency($params);
});
}

Foswiki::Func::registerRESTHandler('purgeCache', sub { return getCore()->purgeCache(@_); },
authenticate => 0,
validate => 0,
http_allow => 'GET,POST',
);

Foswiki::Func::registerRESTHandler('clearCache', sub { return getCore()->clearCache(@_); },
authenticate => 0,
validate => 0,
http_allow => 'GET,POST',
);

return 1;
}

Expand Down
32 changes: 27 additions & 5 deletions lib/Foswiki/Plugins/NumberPlugin/Config.spec
Expand Up @@ -2,9 +2,31 @@
# ---++ NumberPlugin
# This is the configuration used by the <b>NumberPlugin</b>.

# **STRING 50**
# list of currency shortcuts for which a macro are created. E.g. by default an %EUR and %USD macro
# is created which is equivalent to <code>%NUMBER{"%DEFAULT{default="0"}%" type="currency" currency_code="EUR"}%</code>
# and <code>%NUMBER{"%DEFAULT{default="0"}%" type="currency" currency_code="USD"}%</code>.
$Foswiki::cfg{NumberPlugin}{Currencies} = "EUR, USD";
# **SELECT FixerIO,OpenExchangeRates LABEL="Exchange Rates Provider" **
# Select an online service that provides up-to-date exchange rates.
$Foswiki::cfg{NumberPlugin}{RatesProvider} = 'FixerIO';

# **STRING 50 DISPLAY_IF="{NumberPlugin}{RatesProvider}=='FixerIO'" LABEL="Fixer.io Url"**
# Api endpoint for the Fixer.io provider.
$Foswiki::cfg{NumberPlugin}{FixerIO}{Url} = 'https://api.fixer.io/latest';

# **PERL DISPLAY_IF="{NumberPlugin}{RatesProvider}=='FixerIO'" CHECK="undefok" LABEL="Fixer.io Parameter"**
# Extra api parameter for the Fixer.io provider.
$Foswiki::cfg{NumberPlugin}{FixerIO}{Params} = {};

# **STRING 50 DISPLAY_IF="{NumberPlugin}{RatesProvider}=='OpenExchangeRates'" LABEL="OpenExchangeRates Url"**
# Api endpoint for the Fixer.io provider.
$Foswiki::cfg{NumberPlugin}{OpenExchangeRates}{Url} = 'https://openexchangerates.org/api/latest.json';

# **PERL DISPLAY_IF="{NumberPlugin}{OpenExchangeRates}=='OpenExchangeRates'" LABEL="OpenExchangeRates Parameter"**
# Extra api parameter for the OpenExchangeRates provider. Note that you must register at
# https://openexchangerates.org/signup and provide an app_id here
$Foswiki::cfg{NumberPlugin}{OpenExchangeRates}{Params} = {
'app_id' => '???'
};

# **STRING LABEL="Cache Expiration"**
# Expiration time when fetching and caching exchange rates from the provider.
$Foswiki::cfg{NumberPlugin}{CacheExpire} = '1 d';

1;

0 comments on commit 3efa50d

Please sign in to comment.