uc_currency_format hook

Project:Ubercart Contributions
Component:Code
Category:feature request
Priority:normal
Assigned:Unassigned
Status:patch (needs review)
Description
Project: 
Ubercart

uc_currency hook, as described: http://ubercart.org/forum/support/1465/can_ubercart_do

I'm not entirely sure this is the best way to implement it, so please comment. In particular, there can only be one function to hook - it would be possible to run a regular hook invoke, but I'm not sure what you'd do with the values, and how multiple functions should interact (if, for some reason, they exist).

I created uc_currency_format_value() so possibly uc_currency_format_hook() can call it, if it needs to.

--- uc_store.module.orig        2007-10-24 02:25:51.000000000 -0400
+++ uc_store.module     2007-10-24 01:22:28.000000000 -0400
@@ -1855,10 +1855,11 @@
   return $output;
}

+
/**
  * Format an amount for display with the store's currency settings.
  */
-function uc_currency_format($value, $sign = TRUE, $thou = TRUE, $dec = NULL) {
+function uc_currency_format_value($value, $sign = TRUE, $thou = TRUE, $dec = NULL) {
   if (variable_get('uc_currency_prec', 2) > 0) {
     if (abs($value) < '.'. str_repeat('0', variable_get('uc_currency_prec', 2) - 1) .'1') {
       $value = 0;
@@ -1879,6 +1880,23 @@
   if ($sign && variable_get('uc_sign_after_amount', FALSE)) {
     $format .= variable_get('uc_currency_sign', '$');
   }
+
+  return $format;
+}
+
+/**
+ * Format an amount for display with the store's currency settings.
+ * This function is invoked whenever a price is displayed to the user.
+ * It invokes uc_currency_format_hook(), if it exists. which can be used to
+ * convert the displayed currency to another nationality.
+ */
+function uc_currency_format($value, $sign = TRUE, $thou = TRUE, $dec = NULL) {
+  $format = uc_currency_format_value($value, $sign, $thou, $dec);
+
+  // call currency_format_hook if it exists
+  if (function_exists('uc_currency_format_hook')) {
+    $format = uc_currency_format_hook($format, $value, $sign, $thou, $dec);
+  }

   return $format;
}

Sample implementation hook:

if (!function_exists('uc_currency_format_hook')) {
  function uc_currency_format_hook($format, $value, $sign = TRUE, $thou = TRUE, $dec = NULL) {
    return '<strike>'.$format.'</strike>';
  }
}
else {
  watchdog('uc_currency', t('uc_currency_format_hook() has already been defined by another module!'), WATCHDOG_WARNING);
}

possible changes (up for discussion):
- invoke multiple hooks? (what is the expected behaviour when two modules implement the hook?)
- in uc_currency_format(), don't call uc_currency_format_value() if a hook is called? (eg, should hook should do all the work?)

Ryan's picture
Offline
Joined: 08/07/2007
Juice: 15438
Re: uc_currency_format hook

So, the idea with the hook is that you don't have to worry about overlapping function names and modules hard coding conflicting function name checks. I'd need to isolate the purpose of the hook... it seems like you're wanting a third party currency module to take a currency formatted for one system and convert it to a currency formatted for another system. I'm assuming the modules will have a way to parse out what currency you're originating from and converting to?

It seems like you would want to use a real hook here and maybe just have it hardcoded to take the first returned value and report in the watchdog if more than one module return values. (Maybe display the values, even, so the store owner can decide which one they want to use.) But like you pointed out, most stores won't be using more than one service.

So, the hook could be called hook_currency_convert() (or change, or override, or whatever makes sense). Its arguments would be what you've put up. And any module implementing this would return the converted, formatted currency value. Again, I don't know how the module is supposed to know the original and desired currency types.

<?php
/**
* Example hook implementation.
*/
function yahoofinance_currency_convert($format, $value, $sign = TRUE, $thou = TRUE, $dec = NULL) {
 
// Do some conversion magic.
 
return array($converted_currency);
}

/**
* Changes to uc_currency_format().
*/
function uc_currency_format($value, $sign = TRUE, $thou = TRUE, $dec = NULL) {
 
// Do the stuff that gets us $format.

 

$result = module_invoke_all('currency_convert', $format, $value, $sign, $thou, $dec);
  if (
is_array($result) && count($result) > 0) {
    for (
$i = 0; $i < count($result); $i++) {
      if (!empty(
$result[$i])) {
        if (empty(
$new_format)) {
         
$new_format = $result[$i];
        }
        else {
         
watchdog('uc_currency', t('Multiple modules returning converted currency values.'), WATCHDOG_WARNING);
        }
      }
    }
  }

  return empty(

$new_format) ? $format : $new_format;
}
?>

Notice that the hooks must return their values in an array. Then we're polling through all the results looking for any non-empty result string. If we find one, that becomes the new returned format string.

gregmac's picture
Offline
Getting busy with the Ubercode.
Joined: 09/25/2007
Juice: 87
Re: Re: uc_currency_format hook
Assigned to:Ryan» gregmac

Yeah, that looks like it will work with a couple modifications (eg, you'll get the warning messages multiple times per run, if more than 2 modules return a value). Btw, isn't the same behaviour exhibited when the modules don't return an array? (eg, you get the results of all of them back in an array)

The module I was writing is designed to be a general currency conversion module (called uc_currency), and it probably makes sense to be part of ubercart core, but that's up to you guys.

It has an API to convert between two currencies, hooks to invoke various currency conversion lookups (eg, yahoo finance vs webservicex vs google, etc). It caches the results of the lookups, with a configurable TTL. It also has options for formatting (eg, to show "$2.00 USD ($3.00 CAD)" vs the converted value only), and allows you to pick which currencies are available.

The base currency is the store currency - so all prices are expected to be based on that. It will provide a block that allows switching between currencies (likely a dropdown that sets a session variable).

So really what I'm trying to do is implement a fairly robust currency conversion system on top of ubercart, using this uc_currency_format() as the basis for everything since it's already used everywhere in the code. With a hook, it allows ubercart to operate just like it does now if the uc_currency module is not installed.

snelson@drupal.org's picture
Offline
Bug Finder
Joined: 10/04/2007
Juice: 46
I also did a uc_currency module
Assigned to:gregmac» snelson@drupal.org

I too wrote a uc_currency module, only I didn't write a hook, I just overrode uc_currency_format. Obviously a hook is a better way to go.

I was torn on the idea of there being multiple currencies within a country, but the problem I was needing to solve was USD in US, and GBP in UK, that's it. So, I allowed to choose 1 currency per installed country. It also queries yahoo finance and allows for an adjustment rate if so desired. It bases its conversions on the shop default currency, computing everything behind the scenes, and storing transactions in the default currency, but storing the conversion and adjustment rates along with the order for later viewing.

The trickiest part I dealt with was how to do the adjustment rate, since it affects the price on the backend. I ended up doing this in a node api hook, in the load op. Maybe there is a need for a hook_price_alter, or something.

I also have a separate country chooser module. The reason this was separate was because of the problem I was solving. The client need to restrict UK customers to GBP and US to USD. As well, this module detects country automatically by IP using ip_2_cc.

I realize that some of my solution is not ideal for a generic uc_currency module. But, maybe we should combine our code and ideas as contrib and get it just right for core.

Attached is a screenshot of my UI for the module. Its a bit wide, but everything is on one page, which is nice. Just adding a default radio button is all it needs, which could then replace the existing Ubercart currency settings.

AttachmentSize
uc_currency.png 26.24 KB
benkant@drupal.org's picture
Offline
Bug Finder
Joined: 02/28/2008
Juice: 9
I'm also in need of a uc_currency_format hook
Assigned to:snelson@drupal.org» benkant@drupal.org

I need to be able to, like the poster above, display GBP for UK, USD for US, EU for Europe, etc for the customer. Internally the price will be a single currency, as defined in store settings.

Is there any progress on this patch? Or Ryan, perhaps you could implement it as a hook as you laid out in your example?

Either of those examples would do what I wanted, but I'd rather not have to hack ubercart's core to get this functionality. With things moving quite quickly it would be a pain to patch every time another great release is out Eye-wink

Cheers,
Ben

Ryan's picture
Offline
Joined: 08/07/2007
Juice: 15438
Re: I'm also in need of a uc_currency_format hook
Assigned to:benkant@drupal.org» Ryan

Oh man... this is an old one. Laughing out loud I haven't considered this since then, and it never got produced for core inclusion. At the moment, I think we're a little late in the game to add a new hook to core, but if you work out a patch that we can work with in the future that would be awesome.

benkant@drupal.org's picture
Offline
Bug Finder
Joined: 02/28/2008
Juice: 9
Here's a patch
Assigned to:Ryan» benkant@drupal.org

It's your example line for line, which is just what I need.

I don't imagine this hook would do any damage to existing code, and it would make life wonderful if you could commit it for the next minor release! Smiling

Please? Smiling

AttachmentSize
hook_currency_convert.diff 1 KB
Ryan's picture
Offline
Joined: 08/07/2007
Juice: 15438
Re: Here's a patch
Assigned to:benkant@drupal.org» Ryan

Sorry, it's just too late for this... I think I even pointed out some issues with my code as unsolved in that post above.

MPeli's picture
Offline
Joined: 01/27/2011
Juice: 17
#8
Category:» feature request
Assigned to:Ryan» Guest

Is anything of this going to be implemented in Ubercart 3 for Drupal 7?