From: deb.monish Date: Wed, 21 Jun 2017 15:44:58 +0000 (+0530) Subject: CRM-20750: Incorrect financial trxn entries when payment instrument is changed on... X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=5ca657dded2518f9b3613581cde3de696d5b52c4;p=civicrm-core.git CRM-20750: Incorrect financial trxn entries when payment instrument is changed on backoffice Contribution edit form added unit test CRM-20750, fixed db entries ---------------------------------------- * CRM-20750: Incorrect financial trxn entries when payment instrument is changed on backoffice Contribution edit form https://issues.civicrm.org/jira/browse/CRM-20750 --- diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index 374296ed58..15c792e8aa 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -3377,46 +3377,9 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac // instrument is null and now new payment instrument is added along with the payment $params['trxnParams']['payment_instrument_id'] = $params['contribution']->payment_instrument_id; $params['trxnParams']['check_number'] = CRM_Utils_Array::value('check_number', $params); - if (array_key_exists('payment_instrument_id', $params)) { - $params['trxnParams']['total_amount'] = -$trxnParams['total_amount']; - if (CRM_Utils_System::isNull($params['prevContribution']->payment_instrument_id) && - !CRM_Utils_System::isNull($params['contribution']->payment_instrument_id) - ) { - //check if status is changed from Pending to Completed - // do not update payment instrument changes for Pending to Completed - if (!($params['contribution']->contribution_status_id == array_search('Completed', $contributionStatuses) && - in_array($params['prevContribution']->contribution_status_id, $pendingStatus)) - ) { - // for all other statuses create new financial records - self::updateFinancialAccounts($params, 'changePaymentInstrument'); - $params['total_amount'] = $params['trxnParams']['total_amount'] = $trxnParams['total_amount']; - self::updateFinancialAccounts($params, 'changePaymentInstrument'); - $updated = TRUE; - } - } - elseif ((!CRM_Utils_System::isNull($params['contribution']->payment_instrument_id) || - !CRM_Utils_System::isNull($params['prevContribution']->payment_instrument_id)) && - $params['contribution']->payment_instrument_id != $params['prevContribution']->payment_instrument_id - ) { - // for any other payment instrument changes create new financial records - self::updateFinancialAccounts($params, 'changePaymentInstrument'); - $params['total_amount'] = $params['trxnParams']['total_amount'] = $trxnParams['total_amount']; - self::updateFinancialAccounts($params, 'changePaymentInstrument'); - $updated = TRUE; - } - elseif (!CRM_Utils_System::isNull($params['contribution']->check_number) && - $params['contribution']->check_number != $params['prevContribution']->check_number - ) { - // another special case when check number is changed, create new financial records - // create financial trxn with negative amount - $params['trxnParams']['check_number'] = $params['prevContribution']->check_number; - self::updateFinancialAccounts($params, 'changePaymentInstrument'); - // create financial trxn with positive amount - $params['trxnParams']['check_number'] = $params['contribution']->check_number; - $params['total_amount'] = $params['trxnParams']['total_amount'] = $trxnParams['total_amount']; - self::updateFinancialAccounts($params, 'changePaymentInstrument'); - $updated = TRUE; - } + + if (self::isPaymentInstrumentChange($params, $pendingStatus)) { + $updated = CRM_Core_BAO_FinancialTrxn::updateFinancialAccountsOnPaymentInstrumentChange($params); } //if Change contribution amount @@ -3563,24 +3526,6 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac } } } - elseif ($context == 'changePaymentInstrument') { - $params['trxnParams']['net_amount'] = $params['trxnParams']['total_amount']; - $deferredFinancialAccount = CRM_Utils_Array::value('deferred_financial_account_id', $params); - if (empty($deferredFinancialAccount)) { - $deferredFinancialAccount = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($params['prevContribution']->financial_type_id, 'Deferred Revenue Account is'); - } - $lastFinancialTrxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($params['prevContribution']->id, 'DESC', FALSE, NULL, $deferredFinancialAccount); - if (!empty($lastFinancialTrxnId['financialTrxnId'])) { - if ($params['total_amount'] != $params['trxnParams']['total_amount']) { - $params['trxnParams']['to_financial_account_id'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialTrxn', $lastFinancialTrxnId['financialTrxnId'], 'to_financial_account_id'); - $params['trxnParams']['payment_instrument_id'] = $params['prevContribution']->payment_instrument_id; - } - else { - $params['trxnParams']['to_financial_account_id'] = $params['to_financial_account_id']; - $params['trxnParams']['payment_instrument_id'] = $params['contribution']->payment_instrument_id; - } - } - } if ($context == 'changedStatus') { if (($previousContributionStatus == 'Pending' @@ -3687,14 +3632,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac } } } - if ($context == 'changePaymentInstrument') { - // store financial item Proportionaly. - $trxnParams = array( - 'total_amount' => $trxn->total_amount, - 'contribution_id' => $params['contribution']->id, - ); - self::assignProportionalLineItems($trxnParams, $trxn->id, $params['prevContribution']->total_amount); - } + CRM_Core_BAO_FinancialTrxn::createDeferredTrxn(CRM_Utils_Array::value('line_item', $params), $params['contribution'], TRUE, $context); } @@ -5400,6 +5338,46 @@ LEFT JOIN civicrm_contribution on (civicrm_contribution.contact_id = civicrm_co return 1; } + /** + * Does this transaction reflect a payment instrument change. + * + * @param array $params + * @param array $pendingStatuses + * + * @return bool + */ + protected static function isPaymentInstrumentChange(&$params, $pendingStatuses) { + $contributionStatus = CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $params['contribution']->contribution_status_id); + + if (array_key_exists('payment_instrument_id', $params)) { + if (CRM_Utils_System::isNull($params['prevContribution']->payment_instrument_id) && + !CRM_Utils_System::isNull($params['contribution']->payment_instrument_id) + ) { + //check if status is changed from Pending to Completed + // do not update payment instrument changes for Pending to Completed + if (!($contributionStatus == 'Completed' && + in_array($params['prevContribution']->contribution_status_id, $pendingStatuses)) + ) { + return TRUE; + } + } + elseif ((!CRM_Utils_System::isNull($params['contribution']->payment_instrument_id) || + !CRM_Utils_System::isNull($params['prevContribution']->payment_instrument_id)) && + $params['contribution']->payment_instrument_id != $params['prevContribution']->payment_instrument_id + ) { + return TRUE; + } + elseif (!CRM_Utils_System::isNull($params['contribution']->check_number) && + $params['contribution']->check_number != $params['prevContribution']->check_number + ) { + // another special case when check number is changed, create new financial records + // create financial trxn with negative amount + return TRUE; + } + } + return FALSE; + } + /** * Assign Test Value. * diff --git a/CRM/Core/BAO/FinancialTrxn.php b/CRM/Core/BAO/FinancialTrxn.php index 6ccafa5c9f..ebdbfb68e8 100644 --- a/CRM/Core/BAO/FinancialTrxn.php +++ b/CRM/Core/BAO/FinancialTrxn.php @@ -29,8 +29,6 @@ * * @package CRM * @copyright CiviCRM LLC (c) 2004-2017 - * $Id$ - * */ class CRM_Core_BAO_FinancialTrxn extends CRM_Financial_DAO_FinancialTrxn { /** @@ -726,4 +724,52 @@ WHERE ft.is_payment = 1 civicrm_api3('FinancialTrxn', 'create', $trxnparams); } + /** + * The function is responsible for handling financial entries if payment instrument is changed + * + * @param array $inputParams + * + */ + public static function updateFinancialAccountsOnPaymentInstrumentChange($inputParams) { + $prevContribution = $inputParams['prevContribution']; + $currentContribution = $inputParams['contribution']; + // ensure that there are all the information in updated contribution object identified by $currentContribution + $currentContribution->find(TRUE); + + $deferredFinancialAccount = CRM_Utils_Array::value('deferred_financial_account_id', $inputParams); + if (empty($deferredFinancialAccount)) { + $deferredFinancialAccount = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($prevContribution->financial_type_id, 'Deferred Revenue Account is'); + } + + $lastFinancialTrxnId = self::getFinancialTrxnId($prevContribution->id, 'DESC', FALSE, NULL, $deferredFinancialAccount); + + // there is no point to proceed as we can't find the last payment made + if (empty($lastFinancialTrxnId['financialTrxnId'])) { + return FALSE; + } + + // If payment instrument is changed reverse the last payment + // in terms of reversing financial item and trxn + $lastFinancialTrxn = civicrm_api3('FinancialTrxn', 'getsingle', array('id' => $lastFinancialTrxnId['financialTrxnId'])); + unset($lastFinancialTrxn['id']); + $lastFinancialTrxn['trxn_date'] = $inputParams['trxnParams']['trxn_date']; + $lastFinancialTrxn['total_amount'] = -$inputParams['trxnParams']['total_amount']; + $lastFinancialTrxn['net_amount'] = -$inputParams['trxnParams']['net_amount']; + $lastFinancialTrxn['fee_amount'] = -$inputParams['trxnParams']['fee_amount']; + $lastFinancialTrxn['to_financial_account_id'] = CRM_Financial_BAO_FinancialTypeAccount::getInstrumentFinancialAccount($currentContribution->payment_instrument_id); + $lastFinancialTrxn['contribution_id'] = $prevContribution->id; + foreach (array($lastFinancialTrxn, $inputParams['trxnParams']) as $financialTrxnParams) { + $trxn = CRM_Core_BAO_FinancialTrxn::create($financialTrxnParams); + $trxnParams = array( + 'total_amount' => $trxn->total_amount, + 'contribution_id' => $currentContribution->id, + ); + CRM_Contribute_BAO_Contribution::assignProportionalLineItems($trxnParams, $trxn->id, $prevContribution->total_amount); + } + + self::createDeferredTrxn(CRM_Utils_Array::value('line_item', $inputParams), $currentContribution, TRUE, 'changePaymentInstrument'); + + return TRUE; + } + } diff --git a/CRM/Core/Payment/BaseIPN.php b/CRM/Core/Payment/BaseIPN.php index a9f62e75fa..822d748238 100644 --- a/CRM/Core/Payment/BaseIPN.php +++ b/CRM/Core/Payment/BaseIPN.php @@ -421,7 +421,7 @@ class CRM_Core_Payment_BaseIPN { * This function has been problematic for some time but there are now several tests via the api_v3_Contribution test * and the Paypal & Authorize.net IPN tests so any refactoring should be done in conjunction with those. * - * This function needs to have the 'body' moved to the CRM_Contribution_BAO_Contribute class and to undergo + * This function needs to have the 'body' moved to the CRM_Contribute_BAO_Contribute class and to undergo * refactoring to separate the complete transaction and repeat transaction functionality into separate functions with * a shared function that updates related components. * diff --git a/tests/phpunit/CRM/Contribute/Form/ContributionTest.php b/tests/phpunit/CRM/Contribute/Form/ContributionTest.php index f24154d1b8..8bb93faba2 100644 --- a/tests/phpunit/CRM/Contribute/Form/ContributionTest.php +++ b/tests/phpunit/CRM/Contribute/Form/ContributionTest.php @@ -930,6 +930,62 @@ Price Field - Price Field 1 1 $ 100.00 $ 100.00 $this->assertEquals(45, $lineItem['line_total']); } + /** + * Test the submit function if only payment instrument is changed from 'Check' to 'Credit Card' + */ + public function testSubmitUpdateChangePaymentInstrument() { + $form = new CRM_Contribute_Form_Contribution(); + + $form->testSubmit(array( + 'total_amount' => 50, + 'financial_type_id' => 1, + 'receive_date' => '04/21/2015', + 'receive_date_time' => '11:27PM', + 'contact_id' => $this->_individualId, + 'payment_instrument_id' => array_search('Check', $this->paymentInstruments), + 'check_number' => '123AX', + 'contribution_status_id' => 1, + 'price_set_id' => 0, + ), + CRM_Core_Action::ADD); + $contribution = $this->callAPISuccessGetSingle('Contribution', array('contact_id' => $this->_individualId)); + $form->testSubmit(array( + 'total_amount' => 50, + 'net_amount' => 50, + 'financial_type_id' => 1, + 'receive_date' => '04/21/2015', + 'receive_date_time' => '11:27PM', + 'contact_id' => $this->_individualId, + 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments), + 'card_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Financial_DAO_FinancialTrxn', 'card_type_id', 'Visa'), + 'pan_truncation' => '1011', + 'contribution_status_id' => 1, + 'price_set_id' => 0, + 'id' => $contribution['id'], + ), + CRM_Core_Action::UPDATE); + $contribution = $this->callAPISuccessGetSingle('Contribution', array('contact_id' => $this->_individualId)); + $this->assertEquals(50, (int) $contribution['total_amount']); + + $financialTransactions = $this->callAPISuccess('FinancialTrxn', 'get', array('sequential' => TRUE)); + $this->assertEquals(3, $financialTransactions['count']); + + list($oldTrxn, $reversedTrxn, $latestTrxn) = $financialTransactions['values']; + + $this->assertEquals(50, $oldTrxn['total_amount']); + $this->assertEquals('123AX', $oldTrxn['check_number']); + $this->assertEquals(array_search('Check', $this->paymentInstruments), $oldTrxn['payment_instrument_id']); + + $this->assertEquals(-50, $reversedTrxn['total_amount']); + $this->assertEquals('123AX', $reversedTrxn['check_number']); + $this->assertEquals(array_search('Check', $this->paymentInstruments), $reversedTrxn['payment_instrument_id']); + + $this->assertEquals(50, $latestTrxn['total_amount']); + $this->assertEquals('1011', $latestTrxn['pan_truncation']); + $this->assertEquals(array_search('Credit Card', $this->paymentInstruments), $latestTrxn['payment_instrument_id']); + $lineItem = $this->callAPISuccessGetSingle('LineItem', array()); + } + /** * Get parameters for credit card submit calls. * diff --git a/tests/phpunit/api/v3/ContributionTest.php b/tests/phpunit/api/v3/ContributionTest.php index 5112abad4a..b43a99e257 100644 --- a/tests/phpunit/api/v3/ContributionTest.php +++ b/tests/phpunit/api/v3/ContributionTest.php @@ -3396,17 +3396,6 @@ class api_v3_ContributionTest extends CiviUnitTestCase { 'status_id' => 1, ); } - if ($context == 'paymentInstrument') { - $compareParams += array( - 'to_financial_account_id' => CRM_Financial_BAO_FinancialTypeAccount::getInstrumentFinancialAccount(4), - 'payment_instrument_id' => 4, - ); - } - else { - $compareParams['to_financial_account_id'] = 12; - } - $this->assertDBCompareValues('CRM_Financial_DAO_FinancialTrxn', $trxnParams1, array_merge($compareParams, $extraParams)); - $compareParams['total_amount'] = 100; if ($context == 'paymentInstrument') { $compareParams['to_financial_account_id'] = CRM_Financial_BAO_FinancialTypeAccount::getInstrumentFinancialAccount($instrumentId); $compareParams['payment_instrument_id'] = $instrumentId; @@ -3414,6 +3403,8 @@ class api_v3_ContributionTest extends CiviUnitTestCase { else { $compareParams['to_financial_account_id'] = 12; } + $this->assertDBCompareValues('CRM_Financial_DAO_FinancialTrxn', $trxnParams1, array_merge($compareParams, $extraParams)); + $compareParams['total_amount'] = 100; } $this->assertDBCompareValues('CRM_Financial_DAO_FinancialTrxn', $params, array_merge($compareParams, $extraParams));