From 28c249a93ec2af149f389d89b357f09e24bd2f95 Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 6 Aug 2018 12:00:40 +1200 Subject: [PATCH] Fix mishandling of non decimal currency on additional payment form. --- CRM/Contribute/Form/AdditionalPayment.php | 2 +- CRM/Utils/Money.php | 120 +++++++++++++++++++--- 2 files changed, 105 insertions(+), 17 deletions(-) diff --git a/CRM/Contribute/Form/AdditionalPayment.php b/CRM/Contribute/Form/AdditionalPayment.php index 075206d692..d05088c118 100644 --- a/CRM/Contribute/Form/AdditionalPayment.php +++ b/CRM/Contribute/Form/AdditionalPayment.php @@ -174,7 +174,7 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract $defaults['total_amount'] = CRM_Utils_Money::format(abs($this->_refund), NULL, NULL, TRUE); } elseif ($this->_owed) { - $defaults['total_amount'] = number_format($this->_owed, 2); + $defaults['total_amount'] = CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($this->_owed); } // Set $newCredit variable in template to control whether link to credit card mode is included diff --git a/CRM/Utils/Money.php b/CRM/Utils/Money.php index 59168cb606..455562afd2 100644 --- a/CRM/Utils/Money.php +++ b/CRM/Utils/Money.php @@ -95,26 +95,13 @@ class CRM_Utils_Money { if (!$currency) { $currency = $config->defaultCurrency; } - - // money_format() exists only in certain PHP install (CRM-650) - // setlocale() affects native gettext (CRM-11054, CRM-9976) - if (is_numeric($amount) && function_exists('money_format')) { - $lc = setlocale(LC_MONETARY, 0); - setlocale(LC_MONETARY, 'en_US.utf8', 'en_US', 'en_US.utf8', 'en_US', 'C'); - $amount = money_format($valueFormat, $amount); - setlocale(LC_MONETARY, $lc); - } - - $rep = array( - ',' => $config->monetaryThousandSeparator, - '.' => $config->monetaryDecimalPoint, - ); - + $amount = self::formatNumericByFormat($amount, $valueFormat); // If it contains tags, means that HTML was passed and the // amount is already converted properly, // so don't mess with it again. + // @todo deprecate handling for the html tags because .... WTF if (strpos($amount, '<') === FALSE) { - $amount = strtr($amount, $rep); + $amount = self::replaceCurrencySeparators($amount); } $replacements = array( @@ -186,4 +173,105 @@ class CRM_Utils_Money { return TRUE; } + /** + * Format money for display (just numeric part) according to the current locale. + * + * This calls the underlying system function but does not handle currency separators. + * + * It's not totally clear when it changes the $amount value but has historical usage. + * + * @param $amount + * + * @return string + */ + protected static function formatLocaleNumeric($amount) { + return self::formatNumericByFormat($amount, CRM_Core_Config::singleton()->moneyvalueformat); + } + + /** + * Format money for display (just numeric part) according to the current locale with rounding. + * + * At this stage this is conceived as an internal function with the currency wrapper + * functions determining the number of places. + * + * This calls the underlying system function but does not handle currency separators. + * + * It's not totally clear when it changes the $amount value but has historical usage. + * + * @param string $amount + * @param int $numberOfPlaces + * + * @return string + */ + protected static function formatLocaleNumericRounded($amount, $numberOfPlaces) { + return self::formatLocaleNumeric(round($amount, $numberOfPlaces)); + } + + /** + * Format money for display (just numeric part) according to the current locale with rounding. + * + * This handles both rounding & replacement of the currency separators for the locale. + * + * @param string $amount + * @param string $currency + * + * @return string + * Formatted amount. + */ + public static function formatLocaleNumericRoundedByCurrency($amount, $currency) { + $amount = self::formatLocaleNumericRounded($amount, self::getCurrencyPrecision($currency)); + return self::replaceCurrencySeparators($amount); + } + + /** + * Format money for display (just numeric part) according to the current locale with rounding based on the + * default currency for the site. + * + * @param $amount + * @return mixed + */ + public static function formatLocaleNumericRoundedForDefaultCurrency($amount) { + return self::formatLocaleNumericRoundedByCurrency($amount, self::getCurrencyPrecision(CRM_Core_Config::singleton()->defaultCurrency)); + } + + /** + * Replace currency separators. + * + * @param string $amount + * + * @return string + */ + protected static function replaceCurrencySeparators($amount) { + $config = CRM_Core_Config::singleton(); + $rep = array( + ',' => $config->monetaryThousandSeparator, + '.' => $config->monetaryDecimalPoint, + ); + return strtr($amount, $rep); + } + + /** + * Format numeric part of currency by the passed in format. + * + * This is envisaged as an internal function, with wrapper functions defining valueFormat + * into easily understood functions / variables and handling separator conversions and + * rounding. + * + * @param string $amount + * @param string $valueFormat + * + * @return string + */ + protected static function formatNumericByFormat($amount, $valueFormat) { + // money_format() exists only in certain PHP install (CRM-650) + // setlocale() affects native gettext (CRM-11054, CRM-9976) + if (is_numeric($amount) && function_exists('money_format')) { + $lc = setlocale(LC_MONETARY, 0); + setlocale(LC_MONETARY, 'en_US.utf8', 'en_US', 'en_US.utf8', 'en_US', 'C'); + $amount = money_format($valueFormat, $amount); + setlocale(LC_MONETARY, $lc); + } + return $amount; + } + } -- 2.25.1