Fix formatLocaleNumericRoundedByCurrency
authoreileen <emcnaughton@wikimedia.org>
Tue, 8 Sep 2020 19:36:32 +0000 (07:36 +1200)
committereileen <emcnaughton@wikimedia.org>
Tue, 8 Sep 2020 19:58:15 +0000 (07:58 +1200)
The function was rounding by a max of 2 regardless of the number of places passed - it
was only tested for 2 so seemed to work

CRM/Utils/Money.php
tests/phpunit/CRM/Utils/MoneyTest.php

index 9da7a98fe9abcc4bca197eba8e9ae21f9fc9c433..45d2d3fd79cb7c93ac4fed682320947b7f90e180 100644 (file)
@@ -201,7 +201,7 @@ class CRM_Utils_Money {
    * @return string
    */
   protected static function formatLocaleNumericRounded($amount, $numberOfPlaces) {
-    return self::formatLocaleNumeric(round($amount, $numberOfPlaces));
+    return self::formatNumericByFormat($amount, '%!.' . $numberOfPlaces . 'i');
   }
 
   /**
@@ -216,7 +216,42 @@ class CRM_Utils_Money {
    *   Formatted amount.
    */
   public static function formatLocaleNumericRoundedByCurrency($amount, $currency) {
-    $amount = self::formatLocaleNumericRounded($amount, self::getCurrencyPrecision($currency));
+    return self::formatLocaleNumericRoundedByPrecision($amount, self::getCurrencyPrecision($currency));
+  }
+
+  /**
+   * Format money for display (just numeric part) according to the current locale with rounding to the supplied precision.
+   *
+   * This handles both rounding & replacement of the currency separators for the locale.
+   *
+   * @param string $amount
+   * @param int $precision
+   *
+   * @return string
+   *   Formatted amount.
+   */
+  public static function formatLocaleNumericRoundedByPrecision($amount, $precision) {
+    $amount = self::formatLocaleNumericRounded($amount, $precision);
+    return self::replaceCurrencySeparators($amount);
+  }
+
+  /**
+   * Format money for display with rounding to the supplied precision but without padding.
+   *
+   * If the string is shorter than the precision trailing zeros are not added to reach the precision
+   * beyond the 2 required for normally currency formatting.
+   *
+   * This handles both rounding & replacement of the currency separators for the locale.
+   *
+   * @param string $amount
+   * @param int $precision
+   *
+   * @return string
+   *   Formatted amount.
+   */
+  public static function formatLocaleNumericRoundedByOptionalPrecision($amount, $precision) {
+    $decimalPlaces = strlen(substr($amount, strpos($amount, '.') + 1));
+    $amount = self::formatLocaleNumericRounded($amount, $precision > $decimalPlaces ? $decimalPlaces : $precision);
     return self::replaceCurrencySeparators($amount);
   }
 
index 7b97b20b2f7e1314c94a7ae394a33f816270fd06..82cca12ca82167dd73486d42b5b4e490092ed30e 100644 (file)
@@ -72,6 +72,38 @@ class CRM_Utils_MoneyTest extends CiviUnitTestCase {
     $this->setCurrencySeparators(',');
   }
 
+  /**
+   * Test rounded by currency function with specified precision.
+   *
+   * @param string $thousandSeparator
+   *
+   * @dataProvider getThousandSeparators
+   */
+  public function testFormatLocaleNumericRoundedByPrecision($thousandSeparator) {
+    $this->setCurrencySeparators($thousandSeparator);
+    $result = CRM_Utils_Money::formatLocaleNumericRoundedByPrecision(8950.3678, 3);
+    $expected = ($thousandSeparator === ',') ? '8,950.368' : '8.950,368';
+    $this->assertEquals($expected, $result);
+  }
+
+  /**
+   * Test rounded by currency function with specified precision but without padding to reach it.
+   *
+   * @param string $thousandSeparator
+   *
+   * @dataProvider getThousandSeparators
+   */
+  public function testFormatLocaleNumericRoundedByOptionalPrecision($thousandSeparator) {
+    $this->setCurrencySeparators($thousandSeparator);
+    $result = CRM_Utils_Money::formatLocaleNumericRoundedByOptionalPrecision(8950.3678, 8);
+    $expected = ($thousandSeparator === ',') ? '8,950.3678' : '8.950,3678';
+    $this->assertEquals($expected, $result);
+
+    $result = CRM_Utils_Money::formatLocaleNumericRoundedByOptionalPrecision(123456789.987654321, 9);
+    $expected = ($thousandSeparator === ',') ? '123,456,789.98765' : '123.456.789,98765';
+    $this->assertEquals($result, $expected);
+  }
+
   /**
    * Test that using the space character as a currency works
    */