diff --git a/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Accounting.php b/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Accounting.php index a726b1537..3ab03bcf5 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Accounting.php +++ b/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Accounting.php @@ -18,7 +18,7 @@ class Accounting extends Currency * @param ?string $locale Set the locale for the currency format; or leave as the default null. * If provided, Locale values must be a valid formatted locale string (e.g. 'en-GB', 'fr', uz-Arab-AF). * Note that setting a locale will override any other settings defined in this class - * other than the currency code. + * other than the currency code; or decimals (unless the decimals value is set to 0). * * @throws Exception If a provided locale code is not a valid format */ @@ -53,8 +53,12 @@ class Accounting extends Currency // Scrutinizer does not recognize CURRENCY_ACCOUNTING $formatter = new Locale($this->fullLocale, NumberFormatter::CURRENCY_ACCOUNTING); + $mask = $formatter->format(); + if ($this->decimals === 0) { + $mask = (string) preg_replace('/\.0*/', '', $mask); + } - return str_replace('¤', $this->formatCurrencyCode(), $formatter->format()); + return str_replace('¤', $this->formatCurrencyCode(), $mask); } private function icuVersion(): float diff --git a/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Currency.php b/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Currency.php index 957015408..059e9374a 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Currency.php +++ b/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Currency.php @@ -32,7 +32,7 @@ class Currency extends Number * @param ?string $locale Set the locale for the currency format; or leave as the default null. * If provided, Locale values must be a valid formatted locale string (e.g. 'en-GB', 'fr', uz-Arab-AF). * Note that setting a locale will override any other settings defined in this class - * other than the currency code. + * other than the currency code; or decimals (unless the decimals value is set to 0). * * @throws Exception If a provided locale code is not a valid format */ @@ -70,8 +70,12 @@ class Currency extends Number protected function getLocaleFormat(): string { $formatter = new Locale($this->fullLocale, NumberFormatter::CURRENCY); + $mask = $formatter->format(); + if ($this->decimals === 0) { + $mask = (string) preg_replace('/\.0*/', '', $mask); + } - return str_replace('¤', $this->formatCurrencyCode(), $formatter->format()); + return str_replace('¤', $this->formatCurrencyCode(), $mask); } private function formatCurrencyCode(): string diff --git a/tests/PhpSpreadsheetTests/Style/NumberFormat/Wizard/AccountingTest.php b/tests/PhpSpreadsheetTests/Style/NumberFormat/Wizard/AccountingTest.php index 59494f341..894e29b44 100644 --- a/tests/PhpSpreadsheetTests/Style/NumberFormat/Wizard/AccountingTest.php +++ b/tests/PhpSpreadsheetTests/Style/NumberFormat/Wizard/AccountingTest.php @@ -70,6 +70,38 @@ class AccountingTest extends TestCase ]; } + /** + * @dataProvider providerAccountingLocaleNoDecimals + */ + public function testAccountingLocaleNoDecimals( + string $expectedResult, + string $currencyCode, + string $locale + ): void { + if (class_exists(NumberFormatter::class) === false) { + self::markTestSkipped('Intl extension is not available'); + } + + $wizard = new Accounting($currencyCode, 0); + $wizard->setLocale($locale); + self::assertSame($expectedResult, (string) $wizard); + } + + public function providerAccountingLocaleNoDecimals(): array + { + return [ + ["[\$€-fy-NL]\u{a0}#,##0;([\$€-fy-NL]\u{a0}#,##0)", '€', 'fy-NL'], + ["[\$€-nl-NL]\u{a0}#,##0;([\$€-nl-NL]\u{a0}#,##0)", '€', 'nl-NL'], + ["[\$€-nl-BE]\u{a0}#,##0;([\$€-nl-BE]\u{a0}#,##0)", '€', 'NL-BE'], + ["#,##0\u{a0}[\$€-fr-BE];(#,##0\u{a0}[\$€-fr-BE])", '€', 'fr-be'], + ["#,##0\u{a0}[\$€-el-GR]", '€', 'el-gr'], + ['[$$-en-CA]#,##0;([$$-en-CA]#,##0)', '$', 'en-ca'], + ["#,##0\u{a0}[\$\$-fr-CA];(#,##0\u{a0}[\$\$-fr-CA])", '$', 'fr-ca'], + ['[$¥-ja-JP]#,##0;([$¥-ja-JP]#,##0)', '¥', 'ja-JP'], // No decimals to truncate + ["#,##0\u{a0}[\$د.ب‎-ar-BH]", 'د.ب‎', 'ar-BH'], // 3 decimals truncated to none + ]; + } + public function testAccountingLocaleInvalidFormat(): void { if (class_exists(NumberFormatter::class) === false) { diff --git a/tests/PhpSpreadsheetTests/Style/NumberFormat/Wizard/CurrencyTest.php b/tests/PhpSpreadsheetTests/Style/NumberFormat/Wizard/CurrencyTest.php index b31ccc029..ec447d201 100644 --- a/tests/PhpSpreadsheetTests/Style/NumberFormat/Wizard/CurrencyTest.php +++ b/tests/PhpSpreadsheetTests/Style/NumberFormat/Wizard/CurrencyTest.php @@ -69,6 +69,38 @@ class CurrencyTest extends TestCase ]; } + /** + * @dataProvider providerCurrencyLocaleNoDecimals + */ + public function testCurrencyLocaleNoDecimals( + string $expectedResult, + string $currencyCode, + string $locale + ): void { + if (class_exists(NumberFormatter::class) === false) { + self::markTestSkipped('Intl extension is not available'); + } + + $wizard = new Currency($currencyCode, 0); + $wizard->setLocale($locale); + self::assertSame($expectedResult, (string) $wizard); + } + + public function providerCurrencyLocaleNoDecimals(): array + { + return [ + ["[\$€-fy-NL]\u{a0}#,##0;[\$€-fy-NL]\u{a0}#,##0-", '€', 'fy-NL'], // Trailing negative + ["[\$€-nl-NL]\u{a0}#,##0;[\$€-nl-NL]\u{a0}-#,##0", '€', 'nl-NL'], // Sign between currency and value + ["[\$€-nl-BE]\u{a0}#,##0;[\$€-nl-BE]\u{a0}-#,##0", '€', 'NL-BE'], // Sign between currency and value + ["#,##0\u{a0}[\$€-fr-BE]", '€', 'fr-be'], // Trailing currency code + ["#,##0\u{a0}[\$€-el-GR]", '€', 'el-gr'], // Trailing currency code + ['[$$-en-CA]#,##0', '$', 'en-ca'], + ["#,##0\u{a0}[\$\$-fr-CA]", '$', 'fr-ca'], // Trailing currency code + ['[$¥-ja-JP]#,##0', '¥', 'ja-JP'], // No decimals to truncate + ["#,##0\u{a0}[\$د.ب‎-ar-BH]", 'د.ب‎', 'ar-BH'], // 3 decimals truncated to none + ]; + } + public function testCurrencyLocaleInvalidFormat(): void { if (class_exists(NumberFormatter::class) === false) {