Browse Source

Merge pull request #13893 from Zuluru/master

Add support for accounting currency formatting provided by the ICU library
Mark Story 6 years ago
parent
commit
85f7d4e33f
3 changed files with 78 additions and 5 deletions
  1. 1 0
      phpstan.neon
  2. 53 5
      src/I18n/Number.php
  3. 24 0
      tests/TestCase/I18n/NumberTest.php

+ 1 - 0
phpstan.neon

@@ -28,6 +28,7 @@ parameters:
         - '#Binary operation "\+" between array|false and array results in an error#'
         - '#Result of method Cake\\Auth\\BaseAuthenticate::unauthenticated\(\) \(void\) is used#'
         - '#Call to an undefined method DateTimeInterface::setTimezone\(\)#'
+        - '#Access to undefined constant NumberFormatter::CURRENCY_ACCOUNTING#'
     earlyTerminatingMethodCalls:
         Cake\Console\Shell:
             - abort

+ 53 - 5
src/I18n/Number.php

@@ -27,19 +27,27 @@ class Number
 {
     /**
      * Default locale
-     *
-     * @var string
      */
     const DEFAULT_LOCALE = 'en_US';
 
     /**
      * Format type to format as currency
-     *
-     * @var string
      */
     const FORMAT_CURRENCY = 'currency';
 
     /**
+     * Format type to format as currency, accounting style (negative numbers in parentheses)
+     */
+    const FORMAT_CURRENCY_ACCOUNTING = 'currency_accounting';
+
+    /**
+     * ICU Constant for accounting format; not yet widely supported by INTL library.
+     * This will be able to go away once CakePHP minimum PHP requirement is 7.5 or higher.
+     * See UNUM_CURRENCY_ACCOUNTING in https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/unum_8h.html
+     */
+    const CURRENCY_ACCOUNTING = 12;
+
+    /**
      * A list of number formatters indexed by locale and type
      *
      * @var array
@@ -54,6 +62,13 @@ class Number
     protected static $_defaultCurrency;
 
     /**
+     * Default currency format used by Number::currency()
+     *
+     * @var string
+     */
+    protected static $_defaultCurrencyFormat;
+
+    /**
      * Formats a number with a level of precision.
      *
      * Options:
@@ -221,7 +236,7 @@ class Number
             return $options['zero'];
         }
 
-        $formatter = static::formatter(['type' => static::FORMAT_CURRENCY] + $options);
+        $formatter = static::formatter(['type' => static::getDefaultCurrencyFormat()] + $options);
         $abs = abs($value);
         if (!empty($options['fractionSymbol']) && $abs > 0 && $abs < 1) {
             $value *= 100;
@@ -296,6 +311,33 @@ class Number
     }
 
     /**
+     * Getter for default currency format
+     *
+     * @return string Currency Format
+     */
+    public static function getDefaultCurrencyFormat()
+    {
+        if (static::$_defaultCurrencyFormat === null) {
+            static::$_defaultCurrencyFormat = static::FORMAT_CURRENCY;
+        }
+
+        return static::$_defaultCurrencyFormat;
+    }
+
+    /**
+     * Setter for default currency format
+     *
+     * @param string|null $currencyFormat Default currency format to be used by currency()
+     * if $currencyFormat argument is not provided. If null is passed, it will clear the
+     * currently stored value
+     * @return void
+     */
+    public static function setDefaultCurrencyFormat($currencyFormat = null)
+    {
+        static::$_defaultCurrencyFormat = $currencyFormat;
+    }
+
+    /**
      * Returns a formatter object that can be reused for similar formatting task
      * under the same locale and options. This is often a speedier alternative to
      * using other methods in this class as only one formatter object needs to be
@@ -328,6 +370,12 @@ class Number
             $type = $options['type'];
             if ($options['type'] === static::FORMAT_CURRENCY) {
                 $type = NumberFormatter::CURRENCY;
+            } elseif ($options['type'] === static::FORMAT_CURRENCY_ACCOUNTING) {
+                if (defined('NumberFormatter::CURRENCY_ACCOUNTING')) {
+                    $type = NumberFormatter::CURRENCY_ACCOUNTING;
+                } else {
+                    $type = static::CURRENCY_ACCOUNTING;
+                }
             }
         }
 

+ 24 - 0
tests/TestCase/I18n/NumberTest.php

@@ -55,6 +55,7 @@ class NumberTest extends TestCase
         unset($this->Number);
         I18n::setLocale($this->locale);
         Number::setDefaultCurrency();
+        Number::setDefaultCurrencyFormat();
     }
 
     /**
@@ -359,6 +360,29 @@ class NumberTest extends TestCase
     }
 
     /**
+     * Test get default currency format
+     *
+     * @return void
+     */
+    public function testGetDefaultCurrencyFormat()
+    {
+        $this->assertEquals('currency', $this->Number->getDefaultCurrencyFormat());
+    }
+
+    /**
+     * Test set default currency format
+     *
+     * @return void
+     */
+    public function testSetDefaultCurrencyFormat()
+    {
+        $this->Number->setDefaultCurrencyFormat(Number::FORMAT_CURRENCY_ACCOUNTING);
+        $this->assertEquals('currency_accounting', $this->Number->getDefaultCurrencyFormat());
+
+        $this->assertEquals('($123.45)', $this->Number->currency(-123.45));
+    }
+
+    /**
      * testCurrencyCentsNegative method
      *
      * @return void