CRM-20172: Added test and fix for correct amounts.
authorAllen Shaw <allen@JoineryHQ.com>
Fri, 14 Apr 2017 22:24:51 +0000 (17:24 -0500)
committerAllen Shaw <allen@JoineryHQ.com>
Wed, 19 Apr 2017 22:38:29 +0000 (17:38 -0500)
CRM/Contribute/Form/Contribution/Confirm.php
tests/phpunit/api/v3/ContributionPageTest.php

index 55844c1b68d906e028cd64edcb3d54d685a879a9..3e9cb84e0f9038a85dcddff1d1d08d8b777c1c36 100644 (file)
@@ -1475,6 +1475,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
         $isTest,
         $isRecurForFirstTransaction
       );
+      $paymentResults[] = array('contribution_id' => $paymentResult['contribution']->id, 'result' => $paymentResult);
 
       if (!empty($paymentResult['contribution'])) {
         $this->postProcessPremium($premiumParams, $paymentResult['contribution']);
@@ -1644,8 +1645,12 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
       // CRM-19792 : set necessary fields for payment processor
       CRM_Core_Payment_Form::mapParams($form->_bltID, $paymentParams, $paymentParams, TRUE);
 
-      $paymentActionResult = $payment->doPayment($paymentParams, 'contribute');
-      $paymentResults[] = array('contribution_id' => $paymentResult['contribution']->id, 'result' => $paymentActionResult);
+      // If this is a single membership-related contribution, it won't have
+      // be performed yet, so do it now.
+      if ($isPaidMembership && !$isProcessSeparateMembershipTransaction) {
+        $paymentActionResult = $payment->doPayment($paymentParams, 'contribute');
+        $paymentResults[] = array('contribution_id' => $paymentResult['contribution']->id, 'result' => $paymentActionResult);
+      }
       // Do not send an email if Recurring transaction is done via Direct Mode
       // Email will we sent when the IPN is received.
       foreach ($paymentResults as $result) {
@@ -1780,6 +1785,14 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     );
 
     $result = array();
+
+    if ($tempParams['skipLineItem']) {
+      // We are not processing the line item here because we are processing a membership.
+      // So unset the total_amount param, which stores the non-membership contribution amount.
+      $tempParams['total_amount'] = $membershipContribution->total_amount;
+      $tempParams['contributionID'] = $membershipContribution->id;
+    }
+
     if ($form->_values['is_monetary'] && !$form->_params['is_pay_later'] && $minimumFee > 0.0) {
       // At the moment our tests are calling this form in a way that leaves 'object' empty. For
       // now we compensate here.
index 9765868aa0c3713fce9687158c6e5fc9e5be1761..6689dc5331b20f41b9abd30aafb32d73084d725a 100644 (file)
@@ -41,6 +41,7 @@ class api_v3_ContributionPageTest extends CiviUnitTestCase {
   protected $_entity = 'contribution_page';
   protected $contribution_result = NULL;
   protected $_priceSetParams = array();
+  protected $_membershipBlockAmount = 2;
   /**
    * Payment processor details.
    * @var array
@@ -626,6 +627,79 @@ class api_v3_ContributionPageTest extends CiviUnitTestCase {
     $mut->clearMessages();
   }
 
+  /**
+   * Test submit with a membership block in place.
+   *
+   * Ensure a separate payment for the membership vs the contribution, with
+   * correct amounts.
+   */
+  public function testSubmitMembershipBlockIsSeparatePaymentPaymentProcessorNowChargesCorrectAmounts() {
+    $this->setUpMembershipContributionPage(TRUE);
+    $processor = Civi\Payment\System::singleton()->getById($this->_paymentProcessor['id']);
+    $processor->setDoDirectPaymentResult(array('fee_amount' => .72));
+    $test_uniq = uniqid();
+    $contributionPageAmount = 10;
+    $submitParams = array(
+      'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
+      'id' => (int) $this->_ids['contribution_page'],
+      'amount' => $contributionPageAmount,
+      'billing_first_name' => 'Billy',
+      'billing_middle_name' => 'Goat',
+      'billing_last_name' => 'Gruff',
+      'email-Primary' => 'henry@8th.king',
+      'selectMembership' => $this->_ids['membership_type'],
+      'payment_processor_id' => $this->_paymentProcessor['id'],
+      'credit_card_number' => '4111111111111111',
+      'credit_card_type' => 'Visa',
+      'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
+      'cvv2' => 123,
+      'TEST_UNIQ' => $test_uniq,
+    );
+
+    // set custom hook
+    $this->hookClass->setHook('civicrm_alterPaymentProcessorParams', array($this, 'hook_civicrm_alterPaymentProcessorParams'));
+
+    $this->callAPISuccess('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
+    $contributions = $this->callAPISuccess('contribution', 'get', array(
+      'contribution_page_id' => $this->_ids['contribution_page'],
+      'contribution_status_id' => 1,
+    ));
+
+    $result = civicrm_api3('SystemLog', 'get', array(
+      'sequential' => 1,
+      'message' => array('LIKE' => "%{$test_uniq}%"),
+    ));
+    $this->assertCount(2, $result['values'], "Expected exactly 2 log entries matching {$test_uniq}.");
+
+    // Examine logged entries to ensure correct values.
+    $contribution_ids = array();
+    $found_membership_amount = $found_contribution_amount = FALSE;
+    foreach ($result['values'] as $value) {
+      list($junk, $json) = explode("$test_uniq:", $value['message']);
+      $logged_contribution = json_decode($json, TRUE);
+      $contribution_ids[] = $logged_contribution['contributionID'];
+      if (!empty($logged_contribution['total_amount'])) {
+        $amount = $logged_contribution['total_amount'];
+      }
+      else {
+        $amount = $logged_contribution['amount'];
+      }
+
+      if ($amount == $this->_membershipBlockAmount) {
+        $found_membership_amount = TRUE;
+      }
+      if ($amount == $contributionPageAmount) {
+        $found_contribution_amount = TRUE;
+      }
+    }
+
+    $distinct_contribution_ids = array_unique($contribution_ids);
+    $this->assertCount(2, $distinct_contribution_ids, "Expected exactly 2 log contributions with distinct contributionIDs.");
+    $this->assertTrue($found_contribution_amount, "Expected one log contribution with amount '$contributionPageAmount' (the contribution page amount)");
+    $this->assertTrue($found_membership_amount, "Expected one log contribution with amount '$this->_membershipBlockAmount' (the membership amount)");
+
+  }
+
   /**
    * Test that when a transaction fails the pending contribution remains.
    *
@@ -1150,7 +1224,7 @@ class api_v3_ContributionPageTest extends CiviUnitTestCase {
       $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
         'name' => 'membership_amount',
         'label' => 'Membership Amount',
-        'amount' => 2,
+        'amount' => $this->_membershipBlockAmount,
         'financial_type_id' => 'Donation',
         'format.only_id' => TRUE,
         'membership_type_id' => $membershipTypeID,
@@ -1529,4 +1603,24 @@ class api_v3_ContributionPageTest extends CiviUnitTestCase {
     $this->assertEquals($lineItem_TaxAmount, round($submitParams['tax_amount'], 2), 'Wrong Sales Tax Amount is calculated and stored.');
   }
 
+  public function hook_civicrm_alterPaymentProcessorParams($paymentObj, &$rawParams, &$cookedParams) {
+    // Ensure total_amount are the same if they're both given.
+    $total_amount = CRM_Utils_Array::value('total_amount', $rawParams);
+    $amount = CRM_Utils_Array::value('amount', $rawParams);
+    if (!empty($total_amount) && !empty($amount) && $total_amount != $amount) {
+      throw new Exception("total_amount '$total_amount' and amount '$amount' differ.");
+    }
+
+    // Log parameters for later debugging and testing.
+    $message = __FUNCTION__ . ": {$rawParams['TEST_UNIQ']}:";
+    $log_params = array_intersect_key($rawParams, array(
+      'amount' => 1,
+      'total_amount' => 1,
+      'contributionID' => 1,
+    ));
+    $message .= json_encode($log_params);
+    $log = new CRM_Utils_SystemLogger();
+    $log->debug($message, $_REQUEST);
+  }
+
 }