From d5f6fac3361a49da83aef0ac137b0268d38d1343 Mon Sep 17 00:00:00 2001 From: demeritcowboy Date: Sun, 2 Feb 2025 18:29:40 -0500 Subject: [PATCH 1/2] only move funds around if the account changes --- CRM/Financial/Form/PaymentEdit.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CRM/Financial/Form/PaymentEdit.php b/CRM/Financial/Form/PaymentEdit.php index 682b5ea2f909..b33bc8240114 100644 --- a/CRM/Financial/Form/PaymentEdit.php +++ b/CRM/Financial/Form/PaymentEdit.php @@ -214,7 +214,15 @@ protected function submit($submittedValues) { // 1. Record a new reverse financial transaction with old payment instrument // 2. Record a new financial transaction with new payment instrument // 3. Add EntityFinancialTrxn records to relate with corresponding financial item and contribution + $doCreate = FALSE; if ($submittedValues['payment_instrument_id'] != $this->_values['payment_instrument_id']) { + $oldAccount = CRM_Financial_BAO_EntityFinancialAccount::getInstrumentFinancialAccount($this->_values['payment_instrument_id']); + $newAccount = CRM_Financial_BAO_EntityFinancialAccount::getInstrumentFinancialAccount($submittedValues['payment_instrument_id']); + if ($oldAccount != $newAccount) { + $doCreate = TRUE; + } + } + if ($doCreate) { civicrm_api3('Payment', 'cancel', [ 'id' => $this->_values['id'], 'trxn_date' => $submittedValues['trxn_date'], @@ -232,6 +240,12 @@ protected function submit($submittedValues) { else { // simply update the financial trxn civicrm_api3('FinancialTrxn', 'create', $submittedValues + $this->getSubmittedCustomFields()); + // Don't use api since it does too much. + // But also what does payment method field on the contribution mean? + CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution SET payment_instrument_id = %1 WHERE id = %2", [ + 1 => [$submittedValues['payment_instrument_id'], 'Integer'], + 2 => [$this->getContributionID(), 'Integer'], + ]); } CRM_Financial_BAO_Payment::updateRelatedContribution($submittedValues, $this->getContributionID()); From 149d4378b7783526f5dabddb1380907cc984a5dc Mon Sep 17 00:00:00 2001 From: demeritcowboy Date: Mon, 3 Feb 2025 11:39:14 -0500 Subject: [PATCH 2/2] add unit test --- .../CRM/Financial/Form/PaymentEditTest.php | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/phpunit/CRM/Financial/Form/PaymentEditTest.php b/tests/phpunit/CRM/Financial/Form/PaymentEditTest.php index 65e27635c532..776375dd42a6 100644 --- a/tests/phpunit/CRM/Financial/Form/PaymentEditTest.php +++ b/tests/phpunit/CRM/Financial/Form/PaymentEditTest.php @@ -105,6 +105,70 @@ public function testSubmitOnPaymentInstrumentChange(): void { } } + /** + * Same as testSubmitOnPaymentInstrumentChange but when the financial + * account is the same before and after. + * + * @throws \CRM_Core_Exception + * @throws \Civi\Payment\Exception\PaymentProcessorException + */ + public function testSubmitOnPaymentInstrumentChangeSameAccount(): void { + // First create a contribution using 'Check' as payment instrument + $paymentInstrument = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'); + $this->getTestForm('CRM_Contribute_Form_Contribution', [ + 'total_amount' => 50, + 'receive_date' => '2015-04-21 23:27:00', + 'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Donation'), + 'contact_id' => $this->_individualID, + 'payment_instrument_id' => $paymentInstrument, + 'check_number' => '123XA', + 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'), + ])->processForm(); + // fetch the financial trxn record later used in setting default values of payment edit form + $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualID]); + $payments = CRM_Contribute_BAO_Contribution::getPaymentInfo($contribution['id'], 'contribute', TRUE); + $financialTrxnInfo = $payments['transaction'][0]; + + // Create a new payment instrument with the same asset account. + // SpaceChecks are the same as Checks, but in space. + $asset_account = CRM_Financial_BAO_EntityFinancialAccount::getInstrumentFinancialAccount($paymentInstrument); + $forHistoricalReasonsThisNeedsToBePassByRef = [ + 'name' => 'space_check', + 'label' => 'SpaceCheck', + 'financial_account_id' => $asset_account, + 'is_active' => 1, + // It will just pick the next `value` for us so no need to specify `value` + ]; + $newPaymentInstrument = CRM_Core_OptionValue::addOptionValue($forHistoricalReasonsThisNeedsToBePassByRef, 'payment_instrument', CRM_Core_Action::ADD, NULL); + + // Change the payment instrument + $params = [ + 'payment_instrument_id' => $newPaymentInstrument->value, + 'trxn_date' => '2015-04-21 23:27:00', + ]; + $this->getTestForm('CRM_Financial_Form_PaymentEdit', $params, [ + 'contribution_id' => $contribution['id'], + 'id' => $financialTrxnInfo['id'], + ])->processForm(); + + $payments = CRM_Contribute_BAO_Contribution::getPaymentInfo($contribution['id'], 'contribute', TRUE); + $expectedPaymentParams = [ + [ + 'total_amount' => 50.00, + 'financial_type' => 'Donation', + 'payment_instrument' => 'SpaceCheck', + 'status' => 'Completed', + 'receive_date' => '2015-04-21 23:27:00', + ], + ]; + $this->assertCount(1, $payments['transaction']); + foreach ($expectedPaymentParams as $key => $paymentParams) { + foreach ($paymentParams as $fieldName => $expectedValue) { + $this->assertEquals($paymentParams[$fieldName], $payments['transaction'][$key][$fieldName]); + } + } + } + /** * Test to ensure that multiple check_numbers are concatenated * and stored in related contribution's check_number