From c10c47498af769aad35cbd9a504e27a084fdd46b Mon Sep 17 00:00:00 2001 From: Eli Lisseck Date: Wed, 14 Feb 2018 12:40:05 -0800 Subject: [PATCH] CRM-21763 add money util to subtract currency floats with precisely and implement in CRM_Core_BAO_FinancialTrxn::getPartialPaymentWithType --- CRM/Core/BAO/FinancialTrxn.php | 3 +- CRM/Utils/Money.php | 13 +++++++ .../CRM/Core/BAO/FinancialTrxnTest.php | 28 +++++++++++++++ tests/phpunit/CRM/Utils/MoneyTest.php | 36 +++++++++++++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 tests/phpunit/CRM/Utils/MoneyTest.php diff --git a/CRM/Core/BAO/FinancialTrxn.php b/CRM/Core/BAO/FinancialTrxn.php index 922e8ee24b..f61af4f391 100644 --- a/CRM/Core/BAO/FinancialTrxn.php +++ b/CRM/Core/BAO/FinancialTrxn.php @@ -486,7 +486,8 @@ WHERE ft.is_payment = 1 if (!$ftTotalAmt) { $ftTotalAmt = 0; } - $value = $paymentVal = $lineItemTotal - $ftTotalAmt; + $currency = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $contributionId, 'currency'); + $value = $paymentVal = CRM_Utils_Money::subtractCurrencies($lineItemTotal, $ftTotalAmt, $currency); if ($returnType) { $value = array(); if ($paymentVal < 0) { diff --git a/CRM/Utils/Money.php b/CRM/Utils/Money.php index 555e7db6e4..b0771a46f0 100644 --- a/CRM/Utils/Money.php +++ b/CRM/Utils/Money.php @@ -141,4 +141,17 @@ class CRM_Utils_Money { return 2; } + /** + * Subtract currencies using integers instead of floats, to preserve precision + * + * @return float + * Result of subtracting $rightOp from $leftOp to the precision of $currency + */ + public static function subtractCurrencies($leftOp, $rightOp, $currency) { + if (is_numeric($leftOp) && is_numeric($rightOp)) { + $precision = pow(10, self::getCurrencyPrecision($currency)); + return (($leftOp * $precision) - ($rightOp * $precision)) / $precision; + } + } + } diff --git a/tests/phpunit/CRM/Core/BAO/FinancialTrxnTest.php b/tests/phpunit/CRM/Core/BAO/FinancialTrxnTest.php index fdd74846c0..88f71a0262 100644 --- a/tests/phpunit/CRM/Core/BAO/FinancialTrxnTest.php +++ b/tests/phpunit/CRM/Core/BAO/FinancialTrxnTest.php @@ -240,4 +240,32 @@ class CRM_Core_BAO_FinancialTrxnTest extends CiviUnitTestCase { $this->assertEquals($financialTrxn['pan_truncation'], 4567); } + /** + * Test getPartialPaymentWithType function. + */ + public function testGetPartialPaymentWithType() { + //create the contribution that isn't paid yet + $contactId = $this->individualCreate(); + $params = array( + 'contact_id' => $contactId, + 'currency' => 'USD', + 'financial_type_id' => 1, + 'contribution_status_id' => 8, + 'payment_instrument_id' => 4, + 'total_amount' => 300.00, + 'fee_amount' => 0.00, + 'net_amount' => 300.00, + ); + $contribution = $this->callAPISuccess('Contribution', 'create', $params)['values'][7]; + //make a payment one cent short + $params = array( + 'contribution_id' => $contribution['id'], + 'total_amount' => 299.99, + ); + $this->callAPISuccess('Payment', 'create', $params); + //amount owed should be one cent + $amountOwed = CRM_Core_BAO_FinancialTrxn::getPartialPaymentWithType($contribution['id'], 'contribution')['amount_owed']; + $this->assertTrue(0.01 == $amountOwed, 'Amount does not match'); + } + } diff --git a/tests/phpunit/CRM/Utils/MoneyTest.php b/tests/phpunit/CRM/Utils/MoneyTest.php new file mode 100644 index 0000000000..9da7f7f591 --- /dev/null +++ b/tests/phpunit/CRM/Utils/MoneyTest.php @@ -0,0 +1,36 @@ +assertEquals($expectedResult, CRM_Utils_Money::subtractCurrencies($leftOp, $rightOp, $currency)); + } + + /** + * @return array + */ + public function subtractCurrenciesDataProvider() { + return array( + array(number_format(300.00, 2), number_format(299.99, 2), USD, number_format(0.01, 2)), + array(2, 1, USD, 1), + array(0, 0, USD, 0), + array(1, 2, USD, -1), + array(number_format(19.99, 2), number_format(20.00, 2), USD, number_format(-0.01, 2)), + array('notanumber', 5.00, USD, NULL), + ); + } + +} -- 2.25.1