From f577dde1789893a0b7b84174133302d2449e8a60 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Fri, 11 Feb 2022 12:52:25 +0100 Subject: [PATCH] Fix for DOLLARDE() and DOLLARFR() with negative dollar values Additional argument validations --- CHANGELOG.md | 1 + phpstan-baseline.neon | 15 --------- .../Calculation/Financial/Dollar.php | 33 +++++++++++++------ tests/data/Calculation/Financial/DOLLARDE.php | 15 +++++++++ tests/data/Calculation/Financial/DOLLARFR.php | 15 +++++++++ 5 files changed, 54 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08a0937e8a..b10abb0e1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Fixed +- Fix bug with `DOLLARDE()` and `DOLLARFR()` functions when the dollar value is negative [Issue #2578](https://github.com/PHPOffice/PhpSpreadsheet/issues/2578) [PR #2579](https://github.com/PHPOffice/PhpSpreadsheet/pull/2579) - Fix partial function name matching when translating formulae from Russian to English [Issue #2533](https://github.com/PHPOffice/PhpSpreadsheet/issues/2533) [PR #2534](https://github.com/PHPOffice/PhpSpreadsheet/pull/2534) - Various bugs related to Conditional Formatting Rules, and errors in the Xlsx Writer for Conditional Formatting [PR #2491](https://github.com/PHPOffice/PhpSpreadsheet/pull/2491) - Xlsx Reader merge range fixes. diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 6116700532..bb4d186161 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -960,21 +960,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Calculation/Financial/Depreciation.php - - - message: "#^Cannot cast mixed to int\\.$#" - count: 2 - path: src/PhpSpreadsheet/Calculation/Financial/Dollar.php - - - - message: "#^Parameter \\#1 \\$number of function floor expects float, mixed given\\.$#" - count: 2 - path: src/PhpSpreadsheet/Calculation/Financial/Dollar.php - - - - message: "#^Parameter \\#1 \\$x of function fmod expects float, mixed given\\.$#" - count: 2 - path: src/PhpSpreadsheet/Calculation/Financial/Dollar.php - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\Securities\\\\AccruedInterest\\:\\:atMaturity\\(\\) should return float\\|string but returns array\\|string\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Calculation/Financial/Dollar.php b/src/PhpSpreadsheet/Calculation/Financial/Dollar.php index 7bebb39178..74c555ae56 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Dollar.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Dollar.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Financial; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\TextData\Format; @@ -40,18 +41,24 @@ public static function format($number, $precision = 2): string */ public static function decimal($fractionalDollar = null, $fraction = 0) { - $fractionalDollar = Functions::flattenSingleValue($fractionalDollar); - $fraction = (int) Functions::flattenSingleValue($fraction); + try { + $fractionalDollar = FinancialValidations::validateFloat( + Functions::flattenSingleValue($fractionalDollar) ?? 0.0 + ); + $fraction = FinancialValidations::validateInt(Functions::flattenSingleValue($fraction)); + } catch (Exception $e) { + return $e->getMessage(); + } - // Validate parameters - if ($fractionalDollar === null || $fraction < 0) { + // Additional parameter validations + if ($fraction < 0) { return Functions::NAN(); } if ($fraction == 0) { return Functions::DIV0(); } - $dollars = floor($fractionalDollar); + $dollars = ($fractionalDollar < 0.0) ? ceil($fractionalDollar) : floor($fractionalDollar); $cents = fmod($fractionalDollar, 1); $cents /= $fraction; $cents *= 10 ** ceil(log10($fraction)); @@ -76,18 +83,24 @@ public static function decimal($fractionalDollar = null, $fraction = 0) */ public static function fractional($decimalDollar = null, $fraction = 0) { - $decimalDollar = Functions::flattenSingleValue($decimalDollar); - $fraction = (int) Functions::flattenSingleValue($fraction); + try { + $decimalDollar = FinancialValidations::validateFloat( + Functions::flattenSingleValue($decimalDollar) ?? 0.0 + ); + $fraction = FinancialValidations::validateInt(Functions::flattenSingleValue($fraction)); + } catch (Exception $e) { + return $e->getMessage(); + } - // Validate parameters - if ($decimalDollar === null || $fraction < 0) { + // Additional parameter validations + if ($fraction < 0) { return Functions::NAN(); } if ($fraction == 0) { return Functions::DIV0(); } - $dollars = floor($decimalDollar); + $dollars = ($decimalDollar < 0.0) ? ceil($decimalDollar) : floor($decimalDollar); $cents = fmod($decimalDollar, 1); $cents *= $fraction; $cents *= 10 ** (-ceil(log10($fraction))); diff --git a/tests/data/Calculation/Financial/DOLLARDE.php b/tests/data/Calculation/Financial/DOLLARDE.php index eed6c8afd1..7f4c419521 100644 --- a/tests/data/Calculation/Financial/DOLLARDE.php +++ b/tests/data/Calculation/Financial/DOLLARDE.php @@ -3,6 +3,16 @@ // fractional_dollar, fraction, result return [ + [ + 2.5, + 1.6, + 4, + ], + [ + -2.5, + -1.6, + 4, + ], [ 1.125, 1.02, @@ -38,6 +48,11 @@ 1.1200000000000001, 32, ], + [ + '#VALUE!', + 'Not A Number', + 0, + ], [ '#DIV/0!', 1.2344999999999999, diff --git a/tests/data/Calculation/Financial/DOLLARFR.php b/tests/data/Calculation/Financial/DOLLARFR.php index 3d0fb61f1f..42ae133ecf 100644 --- a/tests/data/Calculation/Financial/DOLLARFR.php +++ b/tests/data/Calculation/Financial/DOLLARFR.php @@ -3,6 +3,16 @@ // decimal_dollar, fraction, result return [ + [ + 1.24, + 1.6, + 4, + ], + [ + -1.24, + -1.6, + 4, + ], [ 1.02, 1.125, @@ -38,6 +48,11 @@ 1.375, 32, ], + [ + '#VALUE!', + 'Not A Number', + 0, + ], [ '#DIV/0!', 1.2344999999999999,