Add additional test and fixes to ensure that results are the same when
authoreileen <emcnaughton@wikimedia.org>
Sat, 1 Feb 2020 22:24:36 +0000 (11:24 +1300)
committereileen <emcnaughton@wikimedia.org>
Mon, 3 Feb 2020 21:13:11 +0000 (10:13 +1300)
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
Civi/Test/ContactTestTrait.php
api/v3/Participant.php
tests/phpunit/CRM/Event/Form/ParticipantTest.php

index 3f654b0e7a04901b3a9617435b26bbcfbfdd1def..7cd09259cc78d89a4f583ed3358097559691252f 100644 (file)
@@ -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
index cb64e989e38c9897ea5126f12304fbf455d545ab..03b28597adf6632dbdd69cc67f5ba0a7b50a0bf9 100644 (file)
@@ -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'];
   }
 
   /**
index 29b56ae4ceea3d670f6c7801c398a0e550f96fc3..e70bc067cdcdfdc002b67e3106811ee4f3e83be9 100644 (file)
@@ -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.
index 7e690e0dbbcdc19bc81269e5a8377a22c6e55fec..79f7ee481cd3b67859e3a08619de10d073786a93 100644 (file)
@@ -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'];
+  }
+
 }