From c699d144e20967afa83cee17d6dbf01192b614eb Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Fri, 26 Mar 2021 22:49:16 +0100 Subject: [PATCH] Extract ACCRINT() and ACCRINTM() Financial functions into their own class (#1956) * Extract ACCRINT() and ACCRINTM() Financial functions into their own class Implement additional validations, with additional unit tests Add support for the new calculation method argument for ACCRINT() * Additional tests for Amortization functions --- .../Calculation/Calculation.php | 6 +- .../Engineering/BaseValidations.php | 27 ++++ .../Calculation/Engineering/BesselI.php | 25 +-- .../Calculation/Engineering/BesselJ.php | 25 +-- .../Calculation/Engineering/BesselK.php | 30 ++-- .../Calculation/Engineering/BesselY.php | 30 ++-- .../Calculation/Engineering/Compare.php | 17 ++- .../Calculation/Engineering/Complex.php | 15 +- .../Calculation/Engineering/Erf.php | 6 +- src/PhpSpreadsheet/Calculation/Financial.php | 126 +++++++-------- .../Calculation/Financial/Amortization.php | 27 ++++ .../Calculation/Financial/BaseValidations.php | 72 +++++++++ .../Calculation/Financial/Coupons.php | 76 +--------- .../Calculation/Financial/Depreciation.php | 38 +---- .../Calculation/Financial/Helpers.php | 15 ++ .../Calculation/Financial/InterestRate.php | 25 +-- .../Financial/Securities/AccruedInterest.php | 143 ++++++++++++++++++ .../Financial/Securities/BaseValidations.php | 58 ++++--- .../Financial/Securities/Price.php | 4 +- .../Financial/Securities/Yields.php | 4 +- .../Calculation/Financial/TreasuryBill.php | 14 +- .../Functions/Financial/AccrintMTest.php | 2 +- .../Functions/Financial/AccrintTest.php | 2 +- tests/data/Calculation/Financial/ACCRINT.php | 113 +++++++------- tests/data/Calculation/Financial/ACCRINTM.php | 59 +++++--- .../data/Calculation/Financial/AMORDEGRC.php | 12 ++ .../data/Calculation/Financial/COUPDAYBS.php | 4 +- tests/data/Calculation/Financial/COUPDAYS.php | 4 +- .../data/Calculation/Financial/COUPDAYSNC.php | 4 +- tests/data/Calculation/Financial/COUPNCD.php | 4 +- tests/data/Calculation/Financial/COUPNUM.php | 4 +- tests/data/Calculation/Financial/COUPPCD.php | 4 +- 32 files changed, 625 insertions(+), 370 deletions(-) create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/BaseValidations.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/BaseValidations.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 3cce499f..e1ccb74b 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -233,12 +233,12 @@ class Calculation ], 'ACCRINT' => [ 'category' => Category::CATEGORY_FINANCIAL, - 'functionCall' => [Financial::class, 'ACCRINT'], - 'argumentCount' => '4-7', + 'functionCall' => [Financial\Securities\AccruedInterest::class, 'periodic'], + 'argumentCount' => '4-8', ], 'ACCRINTM' => [ 'category' => Category::CATEGORY_FINANCIAL, - 'functionCall' => [Financial::class, 'ACCRINTM'], + 'functionCall' => [Financial\Securities\AccruedInterest::class, 'atMaturity'], 'argumentCount' => '3-5', ], 'ACOS' => [ diff --git a/src/PhpSpreadsheet/Calculation/Engineering/BaseValidations.php b/src/PhpSpreadsheet/Calculation/Engineering/BaseValidations.php new file mode 100644 index 00000000..48317635 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Engineering/BaseValidations.php @@ -0,0 +1,27 @@ +getMessage(); } - return Functions::VALUE(); + if ($ord < 0) { + return Functions::NAN(); + } + + $fResult = self::calculate($x, $ord); + + return (is_nan($fResult)) ? Functions::NAN() : $fResult; } private static function calculate(float $x, int $ord): float diff --git a/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php b/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php index 5e8bfbf5..ca9ff4f7 100644 --- a/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php +++ b/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php @@ -2,10 +2,13 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; class BesselJ { + use BaseValidations; + /** * BESSELJ. * @@ -30,18 +33,20 @@ class BesselJ $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x); $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord); - if ((is_numeric($x)) && (is_numeric($ord))) { - $ord = (int) floor($ord); - if ($ord < 0) { - return Functions::NAN(); - } - - $fResult = self::calculate((float) $x, $ord); - - return (is_nan($fResult)) ? Functions::NAN() : $fResult; + try { + $x = self::validateFloat($x); + $ord = self::validateInt($ord); + } catch (Exception $e) { + return $e->getMessage(); } - return Functions::VALUE(); + if ($ord < 0) { + return Functions::NAN(); + } + + $fResult = self::calculate($x, $ord); + + return (is_nan($fResult)) ? Functions::NAN() : $fResult; } private static function calculate(float $x, int $ord): float diff --git a/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php b/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php index ff32b78a..faba191f 100644 --- a/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php +++ b/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php @@ -2,10 +2,13 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; class BesselK { + use BaseValidations; + /** * BESSELK. * @@ -28,25 +31,26 @@ class BesselK $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x); $ord = ($ord === null) ? 0 : Functions::flattenSingleValue($ord); - if ((is_numeric($x)) && (is_numeric($ord))) { - $ord = (int) floor($ord); - $x = (float) $x; - if (($ord < 0) || ($x <= 0.0)) { - return Functions::NAN(); - } - - $fBk = self::calculate($x, $ord); - - return (is_nan($fBk)) ? Functions::NAN() : $fBk; + try { + $x = self::validateFloat($x); + $ord = self::validateInt($ord); + } catch (Exception $e) { + return $e->getMessage(); } - return Functions::VALUE(); + if (($ord < 0) || ($x <= 0.0)) { + return Functions::NAN(); + } + + $fBk = self::calculate($x, $ord); + + return (is_nan($fBk)) ? Functions::NAN() : $fBk; } - private static function calculate($x, $ord): float + private static function calculate(float $x, int $ord): float { // special cases - switch (floor($ord)) { + switch ($ord) { case 0: return self::besselK0($x); case 1: diff --git a/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php b/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php index 09694381..1eed5a54 100644 --- a/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php +++ b/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php @@ -2,10 +2,13 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; class BesselY { + use BaseValidations; + /** * BESSELY. * @@ -27,25 +30,26 @@ class BesselY $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x); $ord = ($ord === null) ? 0 : Functions::flattenSingleValue($ord); - if ((is_numeric($x)) && (is_numeric($ord))) { - $ord = (int) floor($ord); - $x = (float) $x; - if (($ord < 0) || ($x <= 0.0)) { - return Functions::NAN(); - } - - $fBy = self::calculate($x, $ord); - - return (is_nan($fBy)) ? Functions::NAN() : $fBy; + try { + $x = self::validateFloat($x); + $ord = self::validateInt($ord); + } catch (Exception $e) { + return $e->getMessage(); } - return Functions::VALUE(); + if (($ord < 0) || ($x <= 0.0)) { + return Functions::NAN(); + } + + $fBy = self::calculate($x, $ord); + + return (is_nan($fBy)) ? Functions::NAN() : $fBy; } - private static function calculate($x, $ord): float + private static function calculate(float $x, int $ord): float { // special cases - switch (floor($ord)) { + switch ($ord) { case 0: return self::besselY0($x); case 1: diff --git a/src/PhpSpreadsheet/Calculation/Engineering/Compare.php b/src/PhpSpreadsheet/Calculation/Engineering/Compare.php index d875174e..c764d8ea 100644 --- a/src/PhpSpreadsheet/Calculation/Engineering/Compare.php +++ b/src/PhpSpreadsheet/Calculation/Engineering/Compare.php @@ -2,10 +2,13 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; class Compare { + use BaseValidations; + /** * DELTA. * @@ -27,8 +30,11 @@ class Compare $a = Functions::flattenSingleValue($a); $b = Functions::flattenSingleValue($b); - if (!is_numeric($a) || !is_numeric($b)) { - return Functions::VALUE(); + try { + $a = self::validateFloat($a); + $b = self::validateFloat($b); + } catch (Exception $e) { + return $e->getMessage(); } return (int) ($a == $b); @@ -54,8 +60,11 @@ class Compare $number = Functions::flattenSingleValue($number); $step = Functions::flattenSingleValue($step); - if (!is_numeric($number) || !is_numeric($step)) { - return Functions::VALUE(); + try { + $number = self::validateFloat($number); + $step = self::validateFloat($step); + } catch (Exception $e) { + return $e->getMessage(); } return (int) ($number >= $step); diff --git a/src/PhpSpreadsheet/Calculation/Engineering/Complex.php b/src/PhpSpreadsheet/Calculation/Engineering/Complex.php index 7dd5ff95..a1a64768 100644 --- a/src/PhpSpreadsheet/Calculation/Engineering/Complex.php +++ b/src/PhpSpreadsheet/Calculation/Engineering/Complex.php @@ -4,10 +4,13 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering; use Complex\Complex as ComplexObject; use Complex\Exception as ComplexException; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; class Complex { + use BaseValidations; + /** * COMPLEX. * @@ -29,10 +32,14 @@ class Complex $imaginary = ($imaginary === null) ? 0.0 : Functions::flattenSingleValue($imaginary); $suffix = ($suffix === null) ? 'i' : Functions::flattenSingleValue($suffix); - if ( - ((is_numeric($realNumber)) && (is_numeric($imaginary))) && - (($suffix == 'i') || ($suffix == 'j') || ($suffix == '')) - ) { + try { + $realNumber = self::validateFloat($realNumber); + $imaginary = self::validateFloat($imaginary); + } catch (Exception $e) { + return $e->getMessage(); + } + + if (($suffix == 'i') || ($suffix == 'j') || ($suffix == '')) { $complex = new ComplexObject($realNumber, $imaginary, $suffix); return (string) $complex; diff --git a/src/PhpSpreadsheet/Calculation/Engineering/Erf.php b/src/PhpSpreadsheet/Calculation/Engineering/Erf.php index 54358ebd..a5df425e 100644 --- a/src/PhpSpreadsheet/Calculation/Engineering/Erf.php +++ b/src/PhpSpreadsheet/Calculation/Engineering/Erf.php @@ -21,8 +21,8 @@ class Erf * Excel Function: * ERF(lower[,upper]) * - * @param float $lower lower bound for integrating ERF - * @param float $upper upper bound for integrating ERF. + * @param mixed (float) $lower lower bound for integrating ERF + * @param mixed (float) $upper upper bound for integrating ERF. * If omitted, ERF integrates between zero and lower_limit * * @return float|string @@ -52,7 +52,7 @@ class Erf * Excel Function: * ERF.PRECISE(limit) * - * @param float $limit bound for integrating ERF + * @param mixed (float) $limit bound for integrating ERF * * @return float|string */ diff --git a/src/PhpSpreadsheet/Calculation/Financial.php b/src/PhpSpreadsheet/Calculation/Financial.php index 084562f8..1a67ef33 100644 --- a/src/PhpSpreadsheet/Calculation/Financial.php +++ b/src/PhpSpreadsheet/Calculation/Financial.php @@ -35,57 +35,58 @@ class Financial * Returns the accrued interest for a security that pays periodic interest. * * Excel Function: - * ACCRINT(issue,firstinterest,settlement,rate,par,frequency[,basis]) + * ACCRINT(issue,firstinterest,settlement,rate,par,frequency[,basis][,calc_method]) + * + * @Deprecated 1.18.0 + * + * @see Financial\Securities\AccruedInterest::periodic() + * Use the periodic() method in the Financial\Securities\AccruedInterest class instead * * @param mixed $issue the security's issue date * @param mixed $firstinterest the security's first interest date * @param mixed $settlement The security's settlement date. - * The security settlement date is the date after the issue date - * when the security is traded to the buyer. + * The security settlement date is the date after the issue date + * when the security is traded to the buyer. * @param mixed (float) $rate the security's annual coupon rate * @param mixed (float) $par The security's par value. - * If you omit par, ACCRINT uses $1,000. - * @param mixed (int) $frequency the number of coupon payments per year. + * If you omit par, ACCRINT uses $1,000. + * @param mixed (int) $frequency The number of coupon payments per year. * Valid frequency values are: * 1 Annual * 2 Semi-Annual * 4 Quarterly * @param mixed (int) $basis The type of day count to use. - * 0 or omitted US (NASD) 30/360 - * 1 Actual/actual - * 2 Actual/360 - * 3 Actual/365 - * 4 European 30/360 + * 0 or omitted US (NASD) 30/360 + * 1 Actual/actual + * 2 Actual/360 + * 3 Actual/365 + * 4 European 30/360 + * @param mixed (bool) $calcMethod + * If true, use Issue to Settlement + * If false, use FirstInterest to Settlement * * @return float|string Result, or a string containing an error */ - public static function ACCRINT($issue, $firstinterest, $settlement, $rate, $par = 1000, $frequency = 1, $basis = 0) - { - $issue = Functions::flattenSingleValue($issue); - $firstinterest = Functions::flattenSingleValue($firstinterest); - $settlement = Functions::flattenSingleValue($settlement); - $rate = Functions::flattenSingleValue($rate); - $par = ($par === null) ? 1000 : Functions::flattenSingleValue($par); - $frequency = ($frequency === null) ? 1 : Functions::flattenSingleValue($frequency); - $basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis); - - // Validate - if ((is_numeric($rate)) && (is_numeric($par))) { - $rate = (float) $rate; - $par = (float) $par; - if (($rate <= 0) || ($par <= 0)) { - return Functions::NAN(); - } - $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis); - if (!is_numeric($daysBetweenIssueAndSettlement)) { - // return date error - return $daysBetweenIssueAndSettlement; - } - - return $par * $rate * $daysBetweenIssueAndSettlement; - } - - return Functions::VALUE(); + public static function ACCRINT( + $issue, + $firstinterest, + $settlement, + $rate, + $par = 1000, + $frequency = 1, + $basis = 0, + $calcMethod = true + ) { + return Securities\AccruedInterest::periodic( + $issue, + $firstinterest, + $settlement, + $rate, + $par, + $frequency, + $basis, + $calcMethod + ); } /** @@ -96,45 +97,28 @@ class Financial * Excel Function: * ACCRINTM(issue,settlement,rate[,par[,basis]]) * + * @Deprecated 1.18.0 + * + * @see Financial\Securities\AccruedInterest::atMaturity() + * Use the atMaturity() method in the Financial\Securities\AccruedInterest class instead + * * @param mixed $issue The security's issue date * @param mixed $settlement The security's settlement (or maturity) date * @param mixed (float) $rate The security's annual coupon rate * @param mixed (float) $par The security's par value. - * If you omit par, ACCRINT uses $1,000. + * If you omit par, ACCRINT uses $1,000. * @param mixed (int) $basis The type of day count to use. - * 0 or omitted US (NASD) 30/360 - * 1 Actual/actual - * 2 Actual/360 - * 3 Actual/365 - * 4 European 30/360 + * 0 or omitted US (NASD) 30/360 + * 1 Actual/actual + * 2 Actual/360 + * 3 Actual/365 + * 4 European 30/360 * * @return float|string Result, or a string containing an error */ public static function ACCRINTM($issue, $settlement, $rate, $par = 1000, $basis = 0) { - $issue = Functions::flattenSingleValue($issue); - $settlement = Functions::flattenSingleValue($settlement); - $rate = Functions::flattenSingleValue($rate); - $par = ($par === null) ? 1000 : Functions::flattenSingleValue($par); - $basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis); - - // Validate - if ((is_numeric($rate)) && (is_numeric($par))) { - $rate = (float) $rate; - $par = (float) $par; - if (($rate <= 0) || ($par <= 0)) { - return Functions::NAN(); - } - $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis); - if (!is_numeric($daysBetweenIssueAndSettlement)) { - // return date error - return $daysBetweenIssueAndSettlement; - } - - return $par * $rate * $daysBetweenIssueAndSettlement; - } - - return Functions::VALUE(); + return Securities\AccruedInterest::atMaturity($issue, $settlement, $rate, $par, $basis); } /** @@ -163,11 +147,11 @@ class Financial * @param float $period The period * @param float $rate Rate of depreciation * @param int $basis The type of day count to use. - * 0 or omitted US (NASD) 30/360 - * 1 Actual/actual - * 2 Actual/360 - * 3 Actual/365 - * 4 European 30/360 + * 0 or omitted US (NASD) 30/360 + * 1 Actual/actual + * 2 Actual/360 + * 3 Actual/365 + * 4 European 30/360 * * @return float|string (string containing the error type if there is an error) */ diff --git a/src/PhpSpreadsheet/Calculation/Financial/Amortization.php b/src/PhpSpreadsheet/Calculation/Financial/Amortization.php index f1a9e3f5..9e838a26 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Amortization.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Amortization.php @@ -3,10 +3,13 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Financial; use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; class Amortization { + use BaseValidations; + /** * AMORDEGRC. * @@ -47,6 +50,18 @@ class Amortization $rate = Functions::flattenSingleValue($rate); $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis); + try { + $cost = self::validateFloat($cost); + $purchased = self::validateDate($purchased); + $firstPeriod = self::validateDate($firstPeriod); + $salvage = self::validateFloat($salvage); + $period = self::validateFloat($period); + $rate = self::validateFloat($rate); + $basis = self::validateBasis($basis); + } catch (Exception $e) { + return $e->getMessage(); + } + $yearFrac = DateTimeExcel\YearFrac::funcYearFrac($purchased, $firstPeriod, $basis); if (is_string($yearFrac)) { return $yearFrac; @@ -113,6 +128,18 @@ class Amortization $rate = Functions::flattenSingleValue($rate); $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis); + try { + $cost = self::validateFloat($cost); + $purchased = self::validateDate($purchased); + $firstPeriod = self::validateDate($firstPeriod); + $salvage = self::validateFloat($salvage); + $period = self::validateFloat($period); + $rate = self::validateFloat($rate); + $basis = self::validateBasis($basis); + } catch (Exception $e) { + return $e->getMessage(); + } + $fOneRate = $cost * $rate; $fCostDelta = $cost - $salvage; // Note, quirky variation for leap years on the YEARFRAC for this function diff --git a/src/PhpSpreadsheet/Calculation/Financial/BaseValidations.php b/src/PhpSpreadsheet/Calculation/Financial/BaseValidations.php new file mode 100644 index 00000000..01d9ab30 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Financial/BaseValidations.php @@ -0,0 +1,72 @@ + 4)) { + throw new Exception(Functions::NAN()); + } + + return $basis; + } +} diff --git a/src/PhpSpreadsheet/Calculation/Financial/Coupons.php b/src/PhpSpreadsheet/Calculation/Financial/Coupons.php index c4a60d90..ce83ccb4 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Coupons.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Coupons.php @@ -2,7 +2,6 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Financial; -use DateTime; use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; @@ -10,6 +9,8 @@ use PhpOffice\PhpSpreadsheet\Shared\Date; class Coupons { + use BaseValidations; + public const FREQUENCY_ANNUAL = 1; public const FREQUENCY_SEMI_ANNUAL = 2; public const FREQUENCY_QUARTERLY = 4; @@ -62,6 +63,9 @@ class Coupons } $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::funcYear($settlement), $basis); + if (is_string($daysPerYear)) { + return Functions::VALUE(); + } $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS); if ($basis === Helpers::DAYS_PER_YEAR_ACTUAL) { @@ -185,7 +189,7 @@ class Coupons if ($basis === Helpers::DAYS_PER_YEAR_NASD) { $settlementDate = Date::excelToDateTimeObject($settlement); - $settlementEoM = self::isLastDayOfMonth($settlementDate); + $settlementEoM = Helpers::isLastDayOfMonth($settlementDate); if ($settlementEoM) { ++$settlement; } @@ -340,26 +344,12 @@ class Coupons return self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS); } - /** - * isLastDayOfMonth. - * - * Returns a boolean TRUE/FALSE indicating if this date is the last date of the month - * - * @param DateTime $testDate The date for testing - * - * @return bool - */ - private static function isLastDayOfMonth(DateTime $testDate) - { - return $testDate->format('d') === $testDate->format('t'); - } - private static function couponFirstPeriodDate($settlement, $maturity, int $frequency, $next) { $months = 12 / $frequency; $result = Date::excelToDateTimeObject($maturity); - $maturityEoM = self::isLastDayOfMonth($result); + $maturityEoM = Helpers::isLastDayOfMonth($result); while ($settlement < Date::PHPToExcel($result)) { $result->modify('-' . $months . ' months'); @@ -375,62 +365,10 @@ class Coupons return Date::PHPToExcel($result); } - private static function validateInputDate($date) - { - $date = DateTimeExcel\Helpers::getDateValue($date); - if (is_string($date)) { - throw new Exception(Functions::VALUE()); - } - - return $date; - } - - private static function validateSettlementDate($settlement) - { - return self::validateInputDate($settlement); - } - - private static function validateMaturityDate($maturity) - { - return self::validateInputDate($maturity); - } - private static function validateCouponPeriod($settlement, $maturity): void { if ($settlement >= $maturity) { throw new Exception(Functions::NAN()); } } - - private static function validateFrequency($frequency): int - { - if (!is_numeric($frequency)) { - throw new Exception(Functions::NAN()); - } - - $frequency = (int) $frequency; - if ( - ($frequency !== self::FREQUENCY_ANNUAL) && - ($frequency !== self::FREQUENCY_SEMI_ANNUAL) && - ($frequency !== self::FREQUENCY_QUARTERLY) - ) { - throw new Exception(Functions::NAN()); - } - - return $frequency; - } - - private static function validateBasis($basis): int - { - if (!is_numeric($basis)) { - throw new Exception(Functions::NAN()); - } - - $basis = (int) $basis; - if (($basis < 0) || ($basis > 4)) { - throw new Exception(Functions::NAN()); - } - - return $basis; - } } diff --git a/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php b/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php index 8770242f..89dc226c 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php @@ -7,6 +7,8 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions; class Depreciation { + use BaseValidations; + /** * DB. * @@ -203,11 +205,7 @@ class Depreciation private static function validateCost($cost, bool $negativeValueAllowed = false): float { - if (!is_numeric($cost)) { - throw new Exception(Functions::VALUE()); - } - - $cost = (float) $cost; + $cost = self::validateFloat($cost); if ($cost < 0.0 && $negativeValueAllowed === false) { throw new Exception(Functions::NAN()); } @@ -217,11 +215,7 @@ class Depreciation private static function validateSalvage($salvage, bool $negativeValueAllowed = false): float { - if (!is_numeric($salvage)) { - throw new Exception(Functions::VALUE()); - } - - $salvage = (float) $salvage; + $salvage = self::validateFloat($salvage); if ($salvage < 0.0 && $negativeValueAllowed === false) { throw new Exception(Functions::NAN()); } @@ -231,11 +225,7 @@ class Depreciation private static function validateLife($life, bool $negativeValueAllowed = false): float { - if (!is_numeric($life)) { - throw new Exception(Functions::VALUE()); - } - - $life = (float) $life; + $life = self::validateFloat($life); if ($life < 0.0 && $negativeValueAllowed === false) { throw new Exception(Functions::NAN()); } @@ -245,11 +235,7 @@ class Depreciation private static function validatePeriod($period, bool $negativeValueAllowed = false): float { - if (!is_numeric($period)) { - throw new Exception(Functions::VALUE()); - } - - $period = (float) $period; + $period = self::validateFloat($period); if ($period <= 0.0 && $negativeValueAllowed === false) { throw new Exception(Functions::NAN()); } @@ -259,11 +245,7 @@ class Depreciation private static function validateMonth($month): int { - if (!is_numeric($month)) { - throw new Exception(Functions::VALUE()); - } - - $month = (int) $month; + $month = self::validateInt($month); if ($month < 1) { throw new Exception(Functions::NAN()); } @@ -273,11 +255,7 @@ class Depreciation private static function validateFactor($factor): float { - if (!is_numeric($factor)) { - throw new Exception(Functions::VALUE()); - } - - $factor = (float) $factor; + $factor = self::validateFloat($factor); if ($factor <= 0.0) { throw new Exception(Functions::NAN()); } diff --git a/src/PhpSpreadsheet/Calculation/Financial/Helpers.php b/src/PhpSpreadsheet/Calculation/Financial/Helpers.php index 08c942ab..79ef61e3 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Helpers.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Helpers.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Financial; +use DateTimeInterface; use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use PhpOffice\PhpSpreadsheet\Calculation\Functions; @@ -47,4 +48,18 @@ class Helpers return Functions::NAN(); } + + /** + * isLastDayOfMonth. + * + * Returns a boolean TRUE/FALSE indicating if this date is the last date of the month + * + * @param DateTimeInterface $date The date for testing + * + * @return bool + */ + public static function isLastDayOfMonth(DateTimeInterface $date) + { + return $date->format('d') === $date->format('t'); + } } diff --git a/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php b/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php index 04b43e32..ed0fec75 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php +++ b/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php @@ -2,10 +2,13 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Financial; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; class InterestRate { + use BaseValidations; + /** * EFFECT. * @@ -25,16 +28,17 @@ class InterestRate $nominalRate = Functions::flattenSingleValue($nominalRate); $periodsPerYear = Functions::flattenSingleValue($periodsPerYear); - // Validate parameters - if (!is_numeric($nominalRate) || !is_numeric($periodsPerYear)) { - return Functions::VALUE(); + try { + $nominalRate = self::validateFloat($nominalRate); + $periodsPerYear = self::validateInt($periodsPerYear); + } catch (Exception $e) { + return $e->getMessage(); } + if ($nominalRate <= 0 || $periodsPerYear < 1) { return Functions::NAN(); } - $periodsPerYear = (int) $periodsPerYear; - return ((1 + $nominalRate / $periodsPerYear) ** $periodsPerYear) - 1; } @@ -53,16 +57,17 @@ class InterestRate $effectiveRate = Functions::flattenSingleValue($effectiveRate); $periodsPerYear = Functions::flattenSingleValue($periodsPerYear); - // Validate parameters - if (!is_numeric($effectiveRate) || !is_numeric($periodsPerYear)) { - return Functions::VALUE(); + try { + $effectiveRate = self::validateFloat($effectiveRate); + $periodsPerYear = self::validateInt($periodsPerYear); + } catch (Exception $e) { + return $e->getMessage(); } + if ($effectiveRate <= 0 || $periodsPerYear < 1) { return Functions::NAN(); } - $periodsPerYear = (int) $periodsPerYear; - // Calculate return $periodsPerYear * (($effectiveRate + 1) ** (1 / $periodsPerYear) - 1); } diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php new file mode 100644 index 00000000..f81ea13c --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php @@ -0,0 +1,143 @@ +getMessage(); + } + + $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis); + if (!is_numeric($daysBetweenIssueAndSettlement)) { + // return date error + return $daysBetweenIssueAndSettlement; + } + $daysBetweenFirstInterestAndSettlement = DateTime::YEARFRAC($firstinterest, $settlement, $basis); + if (!is_numeric($daysBetweenFirstInterestAndSettlement)) { + // return date error + return $daysBetweenFirstInterestAndSettlement; + } + + return $parValue * $rate * $daysBetweenIssueAndSettlement; + } + + /** + * ACCRINTM. + * + * Returns the accrued interest for a security that pays interest at maturity. + * + * Excel Function: + * ACCRINTM(issue,settlement,rate[,par[,basis]]) + * + * @param mixed $issue The security's issue date + * @param mixed $settlement The security's settlement (or maturity) date + * @param mixed (float) $rate The security's annual coupon rate + * @param mixed (float) $par The security's par value. + * If you omit par, ACCRINT uses $1,000. + * @param mixed (int) $basis The type of day count to use. + * 0 or omitted US (NASD) 30/360 + * 1 Actual/actual + * 2 Actual/360 + * 3 Actual/365 + * 4 European 30/360 + * @param mixed $parValue + * + * @return float|string Result, or a string containing an error + */ + public static function atMaturity($issue, $settlement, $rate, $parValue = 1000, $basis = 0) + { + $issue = Functions::flattenSingleValue($issue); + $settlement = Functions::flattenSingleValue($settlement); + $rate = Functions::flattenSingleValue($rate); + $parValue = ($parValue === null) ? 1000 : Functions::flattenSingleValue($parValue); + $basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis); + + try { + $issue = self::validateIssueDate($issue); + $settlement = self::validateSettlementDate($settlement); + self::validateSecurityPeriod($issue, $settlement); + $rate = self::validateRate($rate); + $parValue = self::validateParValue($parValue); + $basis = self::validateBasis($basis); + } catch (Exception $e) { + return $e->getMessage(); + } + + $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis); + if (!is_numeric($daysBetweenIssueAndSettlement)) { + // return date error + return $daysBetweenIssueAndSettlement; + } + + return $parValue * $rate * $daysBetweenIssueAndSettlement; + } +} diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/BaseValidations.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/BaseValidations.php index 2a5e5dd2..bd197d7f 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/BaseValidations.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/BaseValidations.php @@ -7,31 +7,35 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities\Constants as SecuritiesConstants; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -abstract class BaseValidations +trait BaseValidations { - protected static function validateInputDate($date) + protected static function validateDate($date) { - $date = DateTimeExcel\Helpers::getDateValue($date); - if (is_string($date)) { + return DateTimeExcel\Helpers::getDateValue($date); + } + + protected static function validateFloat($value): float + { + if (!is_numeric($value)) { throw new Exception(Functions::VALUE()); } - return $date; + return (float) $value; } protected static function validateSettlementDate($settlement) { - return self::validateInputDate($settlement); + return self::validateDate($settlement); } protected static function validateMaturityDate($maturity) { - return self::validateInputDate($maturity); + return self::validateDate($maturity); } protected static function validateIssueDate($issue) { - return self::validateInputDate($issue); + return self::validateDate($issue); } protected static function validateSecurityPeriod($settlement, $maturity): void @@ -43,11 +47,7 @@ abstract class BaseValidations protected static function validateRate($rate): float { - if (!is_numeric($rate)) { - throw new Exception(Functions::VALUE()); - } - - $rate = (float) $rate; + $rate = self::validateFloat($rate); if ($rate < 0.0) { throw new Exception(Functions::NAN()); } @@ -55,13 +55,19 @@ abstract class BaseValidations return $rate; } - protected static function validatePrice($price): float + protected static function validateParValue($parValue): float { - if (!is_numeric($price)) { - throw new Exception(Functions::VALUE()); + $parValue = self::validateFloat($parValue); + if ($parValue < 0.0) { + throw new Exception(Functions::NAN()); } - $price = (float) $price; + return $parValue; + } + + protected static function validatePrice($price): float + { + $price = self::validateFloat($price); if ($price < 0.0) { throw new Exception(Functions::NAN()); } @@ -71,11 +77,7 @@ abstract class BaseValidations protected static function validateYield($yield): float { - if (!is_numeric($yield)) { - throw new Exception(Functions::VALUE()); - } - - $yield = (float) $yield; + $yield = self::validateFloat($yield); if ($yield < 0.0) { throw new Exception(Functions::NAN()); } @@ -85,11 +87,7 @@ abstract class BaseValidations protected static function validateRedemption($redemption): float { - if (!is_numeric($redemption)) { - throw new Exception(Functions::VALUE()); - } - - $redemption = (float) $redemption; + $redemption = self::validateFloat($redemption); if ($redemption <= 0.0) { throw new Exception(Functions::NAN()); } @@ -99,11 +97,7 @@ abstract class BaseValidations protected static function validateDiscount($discount): float { - if (!is_numeric($discount)) { - throw new Exception(Functions::VALUE()); - } - - $discount = (float) $discount; + $discount = self::validateFloat($discount); if ($discount <= 0.0) { throw new Exception(Functions::NAN()); } diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php index 18a0a2e1..6b04d6d9 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php @@ -8,8 +8,10 @@ use PhpOffice\PhpSpreadsheet\Calculation\Financial\Coupons; use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -class Price extends BaseValidations +class Price { + use BaseValidations; + /** * PRICE. * diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php index 86151904..46c3bb05 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php @@ -7,8 +7,10 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -class Yields extends BaseValidations +class Yields { + use BaseValidations; + /** * YIELDDISC. * diff --git a/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php b/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php index 966500bf..8fd47ba6 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php +++ b/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php @@ -8,6 +8,8 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions; class TreasuryBill { + use BaseValidations; + /** * TBILLEQ. * @@ -29,8 +31,8 @@ class TreasuryBill $discount = Functions::flattenSingleValue($discount); try { - $maturity = DateTimeExcel\Helpers::getDateValue($maturity); - $settlement = DateTimeExcel\Helpers::getDateValue($settlement); + $settlement = self::validateSettlementDate($settlement); + $maturity = self::validateMaturityDate($maturity); } catch (Exception $e) { return $e->getMessage(); } @@ -75,8 +77,8 @@ class TreasuryBill $discount = Functions::flattenSingleValue($discount); try { - $maturity = DateTimeExcel\Helpers::getDateValue($maturity); - $settlement = DateTimeExcel\Helpers::getDateValue($settlement); + $settlement = self::validateSettlementDate($settlement); + $maturity = self::validateMaturityDate($maturity); } catch (Exception $e) { return $e->getMessage(); } @@ -126,8 +128,8 @@ class TreasuryBill $price = Functions::flattenSingleValue($price); try { - $maturity = DateTimeExcel\Helpers::getDateValue($maturity); - $settlement = DateTimeExcel\Helpers::getDateValue($settlement); + $settlement = self::validateSettlementDate($settlement); + $maturity = self::validateMaturityDate($maturity); } catch (Exception $e) { return $e->getMessage(); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/AccrintMTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/AccrintMTest.php index 597db5c2..908e4862 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/AccrintMTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/AccrintMTest.php @@ -21,7 +21,7 @@ class AccrintMTest extends TestCase public function testACCRINTM($expectedResult, ...$args): void { $result = Financial::ACCRINTM(...$args); - self::assertEqualsWithDelta($expectedResult, $result, 1E-8); + self::assertEqualsWithDelta($expectedResult, $result, 1E-12); } public function providerACCRINTM() diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/AccrintTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/AccrintTest.php index edb79230..2d31c4cc 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/AccrintTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/AccrintTest.php @@ -21,7 +21,7 @@ class AccrintTest extends TestCase public function testACCRINT($expectedResult, ...$args): void { $result = Financial::ACCRINT(...$args); - self::assertEqualsWithDelta($expectedResult, $result, 1E-8); + self::assertEqualsWithDelta($expectedResult, $result, 1E-12); } public function providerACCRINT() diff --git a/tests/data/Calculation/Financial/ACCRINT.php b/tests/data/Calculation/Financial/ACCRINT.php index 58f6b636..1852e7e3 100644 --- a/tests/data/Calculation/Financial/ACCRINT.php +++ b/tests/data/Calculation/Financial/ACCRINT.php @@ -4,72 +4,83 @@ return [ [ - 16.666666666666998, - '2008-03-01', - '2008-08-31', - '2008-05-01', - 0.10000000000000001, - 1000, - 2, - 0, + 16.6666666666666, + '2008-03-01', '2008-08-31', '2008-05-01', 0.10, 1000, 2, 0, ], [ - 15.555555555555999, - '2008-03-05', - '2008-08-31', - '2008-05-01', - 0.10000000000000001, - 1000, - 2, - 0, + 15.5555555555559, + '2008-03-05', '2008-08-31', '2008-05-01', 0.10, 1000, 2, 0, + ], + [ + 15.5555555555559, + '2008-03-05', '2008-08-31', '2008-05-01', 0.10, 1000, 2, 0, true, + ], + [ + 7.22222222222222, + '2008-04-05', '2008-08-31', '2008-05-01', 0.10, 1000, 2, 0, true, ], [ 200, - '2010-01-01', - '2010-06-30', - '2010-04-01', - 0.080000000000000002, - 10000, - 4, + '2010-01-01', '2010-06-30', '2010-04-01', 0.08, 10000, 4, + ], + [ + 1600, + '2012-01-01', '2012-04-01', '2013-12-31', 0.08, 10000, 4, + ], + [ + 32.363013698630134, + '2012-01-01', '2012-03-31', '2012-02-15', 0.0525, 5000, 4, 3, 1, + ], + [ + 6.472602739726027, + '2012-01-01', '2012-03-31', '2012-02-15', 0.0525, 1000, 4, 3, 1, + ], + [ + 18.05555555555555, + '2017-08-05', '2017-11-10', '2017-10-10', 0.05, 2000, 4, 0, 1, ], [ '#NUM!', - '2008-03-05', - '2008-08-31', - '2008-05-01', - -0.10000000000000001, - 1000, - 2, - 0, + '2008-03-05', '2008-08-31', '2008-05-01', -0.10, 1000, 2, 0, ], [ '#VALUE!', - 'Invalid Date', - '2008-08-31', - '2008-05-01', - 0.10000000000000001, - 1000, - 2, - 0, + 'Invalid Date', '2008-08-31', '2008-05-01', 0.10, 1000, 2, 0, ], [ '#VALUE!', - '2008-03-01', - '2008-08-31', - '2008-05-01', - 'ABC', - 1000, - 2, - 0, + '2008-03-01', '2008-08-31', '2008-05-01', 'ABC', 1000, 2, 0, ], - [ + 'Non-numeric Rate' => [ '#VALUE!', - '2008-03-01', - '2008-08-31', - '2008-05-01', - 0.10000000000000001, - 1000, - 2, - 'ABC', + '2008-03-01', '2008-08-31', '2008-05-01', 'NaN', 1000, 2, 0, + ], + 'Invalid Rate' => [ + '#NUM!', + '2008-03-01', '2008-08-31', '2008-05-01', -0.10, 1000, 2, 0, + ], + 'Non-numeric Par Value' => [ + '#VALUE!', + '2008-03-01', '2008-08-31', '2008-05-01', 0.10, 'NaN', 2, 0, + ], + 'Invalid Par Value' => [ + '#NUM!', + '2008-03-01', '2008-08-31', '2008-05-01', 0.10, -1000, 2, 0, + ], + 'Non-numeric Frequency' => [ + '#VALUE!', + '2008-03-01', '2008-08-31', '2008-05-01', 0.10, 1000, 'NaN', 0, + ], + 'Invalid Frequency' => [ + '#NUM!', + '2008-03-01', '2008-08-31', '2008-05-01', 0.10, -1000, 3, 0, + ], + 'Non-numeric Basis' => [ + '#VALUE!', + '2008-03-01', '2008-08-31', '2008-05-01', 0.10, 1000, 2, 'ABC', + ], + 'Invalid Basis' => [ + '#NUM!', + '2008-03-01', '2008-08-31', '2008-05-01', 0.10, 1000, 2, -2, ], ]; diff --git a/tests/data/Calculation/Financial/ACCRINTM.php b/tests/data/Calculation/Financial/ACCRINTM.php index 7949b1ad..e442b6f8 100644 --- a/tests/data/Calculation/Financial/ACCRINTM.php +++ b/tests/data/Calculation/Financial/ACCRINTM.php @@ -5,41 +5,50 @@ return [ [ 20.547945205478999, - '2008-04-01', - '2008-06-15', - 0.10000000000000001, - 1000, - 3, + '2008-04-01', '2008-06-15', 0.10, 1000, 3, ], [ 800, - '2010-01-01', - '2010-12-31', - 0.080000000000000002, - 10000, + '2010-01-01', '2010-12-31', 0.08, 10000, + ], + [ + 365.958904109589, + '2012-01-01', '2013-02-15', 0.065, 5000, 3, + ], + [ + 73.1917808219178, + '2012-01-01', '2013-02-15', 0.065, 1000, 3, ], [ '#NUM!', - '2008-03-05', - '2008-08-31', - -0.10000000000000001, - 1000, - 2, + '2008-03-05', '2008-08-31', -0.10, 1000, 2, ], [ '#VALUE!', - 'Invalid Date', - '2008-08-31', - 0.10000000000000001, - 1000, - 2, + 'Invalid Date', '2008-08-31', 0.10, 1000, 2, ], - [ + 'Non-numeric Rate' => [ '#VALUE!', - '2008-03-01', - '2008-08-31', - 'ABC', - 1000, - 2, + '2008-03-01', '2008-08-31', 'NaN', 1000, 2, + ], + 'Invalid Rate' => [ + '#NUM!', + '2008-03-01', '2008-08-31', -0.10, 1000, 2, + ], + 'Non-numeric Par Value' => [ + '#VALUE!', + '2008-03-01', '2008-08-31', 0.10, 'NaN', 2, + ], + 'Invalid Par Value' => [ + '#NUM!', + '2008-03-01', '2008-08-31', 0.10, -1000, 2, + ], + 'Non-numeric Basis' => [ + '#VALUE!', + '2008-03-01', '2008-08-31', 0.10, 1000, 'NaN', + ], + 'Invalid Basis' => [ + '#NUM!', + '2008-03-01', '2008-08-31', 0.10, 1000, 99, ], ]; diff --git a/tests/data/Calculation/Financial/AMORDEGRC.php b/tests/data/Calculation/Financial/AMORDEGRC.php index f4007033..ef3ef1e0 100644 --- a/tests/data/Calculation/Financial/AMORDEGRC.php +++ b/tests/data/Calculation/Financial/AMORDEGRC.php @@ -47,6 +47,14 @@ return [ 42, 150, '2011-01-01', '2011-09-30', 20, 1, 0.4, 4, ], + [ + 2813, + 10000, '2012-03-01', '2012-12-31', 1500, 1, 0.3, 1, + ], + [ + '#VALUE!', + 'NaN', '2012-03-01', '2020-12-25', 20, 1, 0.2, 4, + ], [ '#VALUE!', 550, 'notADate', '2020-12-25', 20, 1, 0.2, 4, @@ -55,4 +63,8 @@ return [ '#VALUE!', 550, '2011-01-01', 'notADate', 20, 1, 0.2, 4, ], + [ + '#VALUE!', + 550, '2012-03-01', '2020-12-25', 'NaN', 1, 0.2, 4, + ], ]; diff --git a/tests/data/Calculation/Financial/COUPDAYBS.php b/tests/data/Calculation/Financial/COUPDAYBS.php index c2208f7d..7a805fdf 100644 --- a/tests/data/Calculation/Financial/COUPDAYBS.php +++ b/tests/data/Calculation/Financial/COUPDAYBS.php @@ -45,7 +45,7 @@ return [ 1, ], 'Non-Numeric Frequency' => [ - '#NUM!', + '#VALUE!', '25-Jan-2007', '15-Nov-2008', 'NaN', @@ -59,7 +59,7 @@ return [ -1, ], 'Non-Numeric Basis' => [ - '#NUM!', + '#VALUE!', '25-Jan-2007', '15-Nov-2008', 4, diff --git a/tests/data/Calculation/Financial/COUPDAYS.php b/tests/data/Calculation/Financial/COUPDAYS.php index acec49d9..2cd2469c 100644 --- a/tests/data/Calculation/Financial/COUPDAYS.php +++ b/tests/data/Calculation/Financial/COUPDAYS.php @@ -59,7 +59,7 @@ return [ 1, ], 'Non-Numeric Frequency' => [ - '#NUM!', + '#VALUE!', '25-Jan-2007', '15-Nov-2008', 'NaN', @@ -73,7 +73,7 @@ return [ -1, ], 'Non-Numeric Basis' => [ - '#NUM!', + '#VALUE!', '25-Jan-2007', '15-Nov-2008', 4, diff --git a/tests/data/Calculation/Financial/COUPDAYSNC.php b/tests/data/Calculation/Financial/COUPDAYSNC.php index 87951dd1..6a7c5bb5 100644 --- a/tests/data/Calculation/Financial/COUPDAYSNC.php +++ b/tests/data/Calculation/Financial/COUPDAYSNC.php @@ -38,7 +38,7 @@ return [ 1, ], 'Non-Numeric Frequency' => [ - '#NUM!', + '#VALUE!', '25-Jan-2007', '15-Nov-2008', 'NaN', @@ -52,7 +52,7 @@ return [ -1, ], 'Non-Numeric Basis' => [ - '#NUM!', + '#VALUE!', '25-Jan-2007', '15-Nov-2008', 4, diff --git a/tests/data/Calculation/Financial/COUPNCD.php b/tests/data/Calculation/Financial/COUPNCD.php index e3d452e5..222c6aa5 100644 --- a/tests/data/Calculation/Financial/COUPNCD.php +++ b/tests/data/Calculation/Financial/COUPNCD.php @@ -38,7 +38,7 @@ return [ 1, ], 'Non-Numeric Frequency' => [ - '#NUM!', + '#VALUE!', '25-Jan-2007', '15-Nov-2008', 'NaN', @@ -52,7 +52,7 @@ return [ -1, ], 'Non-Numeric Basis' => [ - '#NUM!', + '#VALUE!', '25-Jan-2007', '15-Nov-2008', 4, diff --git a/tests/data/Calculation/Financial/COUPNUM.php b/tests/data/Calculation/Financial/COUPNUM.php index b9ad73fa..5af5fd7b 100644 --- a/tests/data/Calculation/Financial/COUPNUM.php +++ b/tests/data/Calculation/Financial/COUPNUM.php @@ -39,7 +39,7 @@ return [ 1, ], 'Non-Numeric Frequency' => [ - '#NUM!', + '#VALUE!', '25-Jan-2007', '15-Nov-2008', 'NaN', @@ -53,7 +53,7 @@ return [ -1, ], 'Non-Numeric Basis' => [ - '#NUM!', + '#VALUE!', '25-Jan-2007', '15-Nov-2008', 4, diff --git a/tests/data/Calculation/Financial/COUPPCD.php b/tests/data/Calculation/Financial/COUPPCD.php index 6d2e2f22..07c00d19 100644 --- a/tests/data/Calculation/Financial/COUPPCD.php +++ b/tests/data/Calculation/Financial/COUPPCD.php @@ -38,7 +38,7 @@ return [ 1, ], 'Non-Numeric Frequency' => [ - '#NUM!', + '#VALUE!', '25-Jan-2007', '15-Nov-2008', 'NaN', @@ -52,7 +52,7 @@ return [ -1, ], 'Non-Numeric Basis' => [ - '#NUM!', + '#VALUE!', '25-Jan-2007', '15-Nov-2008', 4,