From 4132c927cb85d15afa236d0620f30d0f1b959872 Mon Sep 17 00:00:00 2001 From: eileen Date: Sun, 2 Feb 2020 11:24:36 +1300 Subject: [PATCH] Add additional test and fixes to ensure that results are the same when 1) creating a partially paid registration with a pending contribution & then adding a payment to that contribution 2) creating a partially paid registration with a completed contribution for a partial amount 3) created a pending registration with a pending contribution & adding a payment to the contribution. Note that the following bugs previously affected these flows - flow 1 & 2 net_amount was incorrect - flow 1, an extraneous payment was added - flow 3, participant status was not updated from pending to partially paid on adding a payment --- CRM/Financial/BAO/Payment.php | 7 + Civi/Test/ContactTestTrait.php | 7 +- api/v3/Participant.php | 3 + .../CRM/Event/Form/ParticipantTest.php | 133 ++++++++++++++++-- 4 files changed, 139 insertions(+), 11 deletions(-) diff --git a/CRM/Financial/BAO/Payment.php b/CRM/Financial/BAO/Payment.php index 3f654b0e7a..7cd09259cc 100644 --- a/CRM/Financial/BAO/Payment.php +++ b/CRM/Financial/BAO/Payment.php @@ -150,6 +150,13 @@ class CRM_Financial_BAO_Payment { } elseif ($contributionStatus === 'Pending' && $params['total_amount'] > 0) { self::updateContributionStatus($contribution['id'], 'Partially Paid'); + $participantPayments = civicrm_api3('ParticipantPayment', 'get', [ + 'contribution_id' => $contribution['id'], + 'participant_id.status_id' => ['IN' => ['Pending from pay later', 'Pending from incomplete transaction']], + ])['values']; + foreach ($participantPayments as $participantPayment) { + civicrm_api3('Participant', 'create', ['id' => $participantPayment['participant_id'], 'status_id' => 'Partially paid']); + } } elseif ($contributionStatus === 'Completed' && ((float) CRM_Core_BAO_FinancialTrxn::getTotalPayments($contribution['id'], TRUE) === 0.0)) { // If the contribution has previously been completed (fully paid) and now has total payments adding up to 0 diff --git a/Civi/Test/ContactTestTrait.php b/Civi/Test/ContactTestTrait.php index cb64e989e3..03b28597ad 100644 --- a/Civi/Test/ContactTestTrait.php +++ b/Civi/Test/ContactTestTrait.php @@ -156,16 +156,17 @@ trait ContactTestTrait { * For civicrm_contact_add api function call. * * @return int - * id of Household created - * @throws \CRM_Core_Exception + * id of contact created * + * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception */ private function _contactCreate($params) { $result = civicrm_api3('contact', 'create', $params); if (!empty($result['is_error']) || empty($result['id'])) { throw new \CRM_Core_Exception('Could not create test contact, with message: ' . \CRM_Utils_Array::value('error_message', $result) . "\nBacktrace:" . \CRM_Utils_Array::value('trace', $result)); } - return $result['id']; + return (int) $result['id']; } /** diff --git a/api/v3/Participant.php b/api/v3/Participant.php index 29b56ae4ce..e70bc067cd 100644 --- a/api/v3/Participant.php +++ b/api/v3/Participant.php @@ -23,6 +23,9 @@ * * @return array * API result array + * + * @throws \CiviCRM_API3_Exception + * @throws \CRM_Core_Exception */ function civicrm_api3_participant_create($params) { // Check that event id is not an template - should be done @ BAO layer. diff --git a/tests/phpunit/CRM/Event/Form/ParticipantTest.php b/tests/phpunit/CRM/Event/Form/ParticipantTest.php index 7e690e0dbb..79f7ee481c 100644 --- a/tests/phpunit/CRM/Event/Form/ParticipantTest.php +++ b/tests/phpunit/CRM/Event/Form/ParticipantTest.php @@ -340,11 +340,11 @@ class CRM_Event_Form_ParticipantTest extends CiviUnitTestCase { $event = $this->eventCreate($eventParams); } - $contactID = $this->individualCreate(); + $this->ids['contact']['event'] = (int) $this->individualCreate(); /** @var CRM_Event_Form_Participant $form */ $form = $this->getFormObject('CRM_Event_Form_Participant'); $form->_single = TRUE; - $form->_contactID = $form->_contactId = $contactID; + $form->_contactID = $form->_contactId = $this->ids['contact']['event']; $form->setCustomDataTypes(); $form->_eventId = $event['id']; if (!empty($eventParams['is_monetary'])) { @@ -547,7 +547,6 @@ class CRM_Event_Form_ParticipantTest extends CiviUnitTestCase { * @param bool $isQuickConfig * * @throws \CRM_Core_Exception - * @throws \CiviCRM_API3_Exception */ public function testSubmitPartialPayment($isQuickConfig) { $mut = new CiviMailUtils($this, TRUE); @@ -573,7 +572,7 @@ class CRM_Event_Form_ParticipantTest extends CiviUnitTestCase { 'hidden_custom' => '1', 'hidden_custom_group_count' => ['' => 1], 'custom_4_-1' => '', - 'contact_id' => $form->_contactID, + 'contact_id' => $this->getContactID(), 'event_id' => $this->getEventID(), 'campaign_id' => '', 'register_date' => '2020-01-31 00:50:00', @@ -584,9 +583,77 @@ class CRM_Event_Form_ParticipantTest extends CiviUnitTestCase { 'MAX_FILE_SIZE' => '33554432', ]; $form->submit($submitParams); + $this->assertPartialPaymentResult($isQuickConfig, $mut); + } + + /** + * Test submitting a partially paid event registration, recording a pending contribution. + * + * This tests + * + * @dataProvider getBooleanDataProvider + * + * @param bool $isQuickConfig + * + * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception + */ + public function testSubmitPendingPartiallyPaidAddPayment($isQuickConfig) { + $mut = new CiviMailUtils($this, TRUE); + $form = $this->getForm(['is_monetary' => 1]); + $this->callAPISuccess('PriceSet', 'create', ['is_quick_config' => $isQuickConfig, 'id' => $this->getPriceSetID()]); + $paymentInstrumentID = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'); + $submitParams = $this->getRecordContributionParams('Partially paid', $form); + $form->submit($submitParams); + $this->callAPISuccess('Payment', 'create', [ + 'contribution_id' => $this->callAPISuccessGetValue('Contribution', ['return' => 'id']), + 'total_amount' => 20, + 'check_number' => 879, + 'payment_instrument_id' => $paymentInstrumentID, + ]); + $this->assertPartialPaymentResult($isQuickConfig, $mut); + } + + /** + * Test submitting a partially paid event registration, recording a pending contribution. + * + * This tests + * + * @dataProvider getBooleanDataProvider + * + * @param bool $isQuickConfig + * + * @throws \CRM_Core_Exception + */ + public function testSubmitPendingAddPayment($isQuickConfig) { + $mut = new CiviMailUtils($this, TRUE); + $form = $this->getForm(['is_monetary' => 1]); + $this->callAPISuccess('PriceSet', 'create', ['is_quick_config' => $isQuickConfig, 'id' => $this->getPriceSetID()]); + $paymentInstrumentID = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'); + $submitParams = $this->getRecordContributionParams('Pending from pay later', 'Pending'); + // Create the pending contribution for the full amount to be paid. + $submitParams['total_amount'] = 1550.55; + $form->submit($submitParams); + $this->callAPISuccess('Payment', 'create', [ + 'contribution_id' => $this->callAPISuccessGetValue('Contribution', ['return' => 'id']), + 'total_amount' => 20, + 'check_number' => 879, + 'payment_instrument_id' => $paymentInstrumentID, + ]); + $this->assertPartialPaymentResult($isQuickConfig, $mut, FALSE); + } + + /** + * @param bool $isQuickConfig + * @param \CiviMailUtils $mut + * @param bool $isAmountPaidOnForm + * Was the amount paid entered on the form (if so this should be on the receipt) + */ + protected function assertPartialPaymentResult($isQuickConfig, CiviMailUtils $mut, $isAmountPaidOnForm = TRUE) { + $paymentInstrumentID = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'); $contribution = $this->callAPISuccessGetSingle('Contribution', []); $expected = [ - 'contact_id' => $form->_contactID, + 'contact_id' => $this->getContactID(), 'total_amount' => '1550.55', 'fee_amount' => '0.00', 'net_amount' => '1550.55', @@ -602,7 +669,7 @@ class CRM_Event_Form_ParticipantTest extends CiviUnitTestCase { $participant = $this->callAPISuccessGetSingle('Participant', []); $this->assertAttributesEquals([ - 'contact_id' => $form->_contactID, + 'contact_id' => $this->getContactID(), 'event_title' => 'Annual CiviCRM meet', 'participant_fee_level' => [0 => 'big - 1'], 'participant_fee_amount' => '1550.55', @@ -646,7 +713,7 @@ class CRM_Event_Form_ParticipantTest extends CiviUnitTestCase { $financialItem = $this->callAPISuccessGetSingle('FinancialItem', []); $this->assertAttributesEquals([ 'description' => 'big', - 'contact_id' => $form->_contactID, + 'contact_id' => $this->getContactID(), 'amount' => 1550.55, 'currency' => 'USD', 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Financial_BAO_FinancialItem', 'status_id', 'Unpaid'), @@ -664,7 +731,7 @@ class CRM_Event_Form_ParticipantTest extends CiviUnitTestCase { 'Annual CiviCRM meet', 'Registered Email', $isQuickConfig ? $this->formatMoneyInput(1550.55) . ' big - 1' : 'Price Field - big', - 'Total Paid: $ 20.00', + $isAmountPaidOnForm ? 'Total Paid: $ 20.00' : ' ', 'Balance: $ 1,530.55', 'Financial Type: Event Fee', 'Paid By: Check', @@ -708,6 +775,47 @@ class CRM_Event_Form_ParticipantTest extends CiviUnitTestCase { return (int) $this->_ids['price_field_value'][1]; } + /** + * Get the parameters for recording a contribution. + * + * @param string $participantStatus + * @param string $contributionStatus + * + * @return array + */ + protected function getRecordContributionParams($participantStatus, $contributionStatus): array { + $submitParams = [ + 'hidden_feeblock' => '1', + 'hidden_eventFullMsg' => '', + 'priceSetId' => $this->getPriceSetID(), + $this->getPriceFieldKey() => $this->getPriceFieldValueID(), + 'check_number' => '879', + 'record_contribution' => '1', + 'financial_type_id' => '4', + 'receive_date' => '2020-01-31 00:51:00', + 'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'), + 'trxn_id' => '', + 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $contributionStatus), + 'total_amount' => '20', + 'send_receipt' => '1', + 'from_email_address' => $this->getFromEmailAddress(), + 'receipt_text' => 'Contact the Development Department if you need to make any changes to your registration.', + 'hidden_custom' => '1', + 'hidden_custom_group_count' => ['' => 1], + 'custom_4_-1' => '', + 'contact_id' => $this->getContactID(), + 'event_id' => $this->getEventID(), + 'campaign_id' => '', + 'register_date' => '2020-01-31 00:50:00', + 'role_id' => [0 => CRM_Core_PseudoConstant::getKey('CRM_Event_BAO_Participant', 'role_id', 'Attendee')], + 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Event_BAO_Participant', 'status_id', $participantStatus), + 'source' => 'I wrote this', + 'note' => 'I wrote a note', + 'MAX_FILE_SIZE' => '33554432', + ]; + return $submitParams; + } + /** * Get the id of the created event. * @@ -717,4 +825,13 @@ class CRM_Event_Form_ParticipantTest extends CiviUnitTestCase { return $this->ids['event']['event']; } + /** + * Get created contact ID. + * + * @return int + */ + protected function getContactID(): int { + return $this->ids['contact']['event']; + } + } -- 2.25.1