Improve test cover for full form flow of separate payment
authorEileen McNaughton <emcnaughton@wikimedia.org>
Sat, 18 Nov 2023 06:23:32 +0000 (19:23 +1300)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Sun, 19 Nov 2023 20:25:51 +0000 (09:25 +1300)
CRM/Contribute/Form/Contribution/Confirm.php
CRM/Contribute/Form/Contribution/Main.php
Civi/Test/ContributionPageTestTrait.php
templates/CRM/Contribute/Form/Contribution/Main.tpl
tests/phpunit/CRM/Contribute/Form/Contribution/ConfirmTest.php
tests/phpunit/api/v3/ContributionPageTest.php

index 3b6df20e2874b289c0a41e031ce0d3e2d820372f..d46780709312eedba07ca8357eb6b9c76e8e6c8d 100644 (file)
@@ -274,7 +274,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     $this->_params['ip_address'] = CRM_Utils_System::ipAddress();
     $this->_params['amount'] = $this->get('amount');
     if (isset($this->_params['amount'])) {
-      $this->setFormAmountFields($this->_params['priceSetId']);
+      $this->setFormAmountFields($this->getPriceSetID());
     }
 
     $this->_useForMember = $this->get('useForMember');
@@ -1855,7 +1855,6 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
    * @param int $priceSetID
    */
   public function setFormAmountFields($priceSetID) {
-    $isQuickConfig = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_params['priceSetId'], 'is_quick_config');
     $priceField = new CRM_Price_DAO_PriceField();
     $priceField->price_set_id = $priceSetID;
     $priceField->orderBy('weight');
@@ -1866,7 +1865,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
       if ($priceField->name == "contribution_amount") {
         $paramWeDoNotUnderstand = $priceField->id;
       }
-      if ($isQuickConfig && !empty($this->_params["price_{$priceField->id}"])) {
+      if ($this->isQuickConfig() && !empty($this->_params["price_{$priceField->id}"])) {
         if ($this->_values['fee'][$priceField->id]['html_type'] != 'Text') {
           // @todo - stop setting amount level in this function & call the CRM_Price_BAO_PriceSet::getAmountLevel
           // function to get correct amount level consistently. Remove setting of the amount level in
index 327cecf8d95ad49cbd1aea40b10cd838f5a1d203..0b2ee65d92c0d11690569e880e9f57a46972fa3c 100644 (file)
@@ -1215,7 +1215,7 @@ class CRM_Contribute_Form_Contribution_Main extends CRM_Contribute_Form_Contribu
 
     if ($this->isQuickConfig()) {
       $priceField = new CRM_Price_DAO_PriceField();
-      $priceField->price_set_id = $params['priceSetId'];
+      $priceField->price_set_id = $this->getPriceSetID();
       $priceField->orderBy('weight');
       $priceField->find();
 
index 19dd23166a39af89db42bbbe8f1665aa5649d8a3..7d6a0d1cf63d774a821703d27678b96d8691c28d 100644 (file)
@@ -11,6 +11,7 @@
 
 namespace Civi\Test;
 
+use Civi\API\EntityLookupTrait;
 use Civi\Api4\UFField;
 use Civi\Api4\UFGroup;
 use Civi\Api4\UFJoin;
@@ -33,6 +34,7 @@ use Civi\Api4\UFJoin;
  */
 trait ContributionPageTestTrait {
   use EntityTrait;
+  use EntityLookupTrait;
 
   /**
    * Create a contribution page for test purposes.
@@ -57,6 +59,7 @@ trait ContributionPageTestTrait {
     ];
     $contributionPageValues += $contributionPageDefaults;
     $return = $this->createTestEntity('ContributionPage', $contributionPageValues, $identifier);
+    $this->define('ContributionPage', 'ContributionPage_' . $identifier, $return);
     $this->addProfilesToContributionPage();
     return $return;
   }
@@ -260,6 +263,128 @@ trait ContributionPageTestTrait {
     ], 'check_box');
   }
 
+  /**
+   * Set up a contribution page configured with quick config.
+   *
+   * The created price set has up to 3 fields.
+   *
+   * - Radio field (key = 'contribution_amount') with 3 options ('contribution_amount_25','contribution_amount_15','contribution_amount_0'), financial type ID matches the page financial type.
+   * - Text field ('other_amount') with amount = 1 - ie if qty is 2 then amount is 2, financial type ID matches the page financial type.
+   * - Radio field (key = 'membership_amount') with one option per enabled membership type (General will be created if not exists).
+   *
+   * @param array $contributionPageParameters
+   * @param array $priceSetParameters
+   * @param bool $isSeparatePayment
+   * @param bool $membershipAmountField
+   *  - use false to suppress the creation of this field.
+   * @param bool $contributionAmountField
+   * - use false to suppress the creation of this field.
+   * @param bool $otherAmountField
+   * - use false to suppress the creation of this field.
+   * @param string $identifier
+   *
+   * @throws \CRM_Core_Exception
+   * @noinspection PhpUnhandledExceptionInspection
+   */
+  public function contributionPageQuickConfigCreate(array $contributionPageParameters = [], array $priceSetParameters = [], bool $isSeparatePayment = FALSE, bool $membershipAmountField = TRUE, bool $contributionAmountField = TRUE, bool $otherAmountField = TRUE, string $identifier = 'QuickConfig'): void {
+    $this->contributionPageCreatePaid($contributionPageParameters, $priceSetParameters, $identifier);
+    $priceSetID = $this->ids['PriceSet']['QuickConfig'];
+    if ($membershipAmountField !== FALSE) {
+      $priceField = $this->createTestEntity('PriceField', [
+        'price_set_id' => $priceSetID,
+        'label' => 'Membership Amount',
+        'html_type' => 'Radio',
+        'name' => 'membership_amount',
+      ], 'membership_amount');
+      $membershipTypes = \CRM_Member_BAO_MembershipType::getAllMembershipTypes();
+      if (empty($membershipTypes)) {
+        $this->createTestEntity('MembershipType', [
+          'name' => 'General',
+          'duration_unit' => 'year',
+          'duration_interval' => 1,
+          'period_type' => 'rolling',
+          'member_of_contact_id' => \CRM_Core_BAO_Domain::getDomain()->contact_id,
+          'domain_id' => 1,
+          'financial_type_id:name' => 'Member Dues',
+          'is_active' => 1,
+          'sequential' => 1,
+          'minimum_fee' => 100,
+          'visibility' => 'Public',
+        ]);
+        $membershipTypes = \CRM_Member_BAO_MembershipType::getAllMembershipTypes();
+      }
+      foreach ($membershipTypes as $membershipType) {
+        $name = 'membership_' . strtolower($membershipType['name']);
+        $this->createTestEntity('PriceFieldValue', [
+          'name' => 'membership_' . $name,
+          'label' => 'Membership Amount',
+          'amount' => $membershipType['minimum_fee'],
+          'financial_type_id:name' => 'Member Dues',
+          'format.only_id' => TRUE,
+          'membership_type_id' => $membershipType['id'],
+          'price_field_id' => $priceField['id'],
+        ], $name);
+      }
+      $this->createTestEntity('MembershipBlock', [
+        'entity_id' => $this->getContributionPageID(),
+        'entity_table' => 'civicrm_contribution_page',
+        'is_required' => TRUE,
+        'is_active' => TRUE,
+        'is_separate_payment' => $isSeparatePayment,
+        'membership_type_default' => reset($this->ids['MembershipType']),
+        'membership_types' => array_fill_keys(array_keys($membershipTypes), 1),
+      ]);
+    }
+    if ($contributionAmountField !== FALSE) {
+      $priceField = $this->createTestEntity('PriceField', [
+        'price_set_id' => $priceSetID,
+        'label' => 'Contribution Amount',
+        'html_type' => 'Radio',
+        'name' => 'contribution_amount',
+      ], 'contribution_amount');
+      $this->createTestEntity('PriceFieldValue', [
+        'price_field_id' => $priceField['id'],
+        'label' => 'Fifteen',
+        'name' => 'contribution_amount_15',
+        'amount' => 15,
+        'non_deductible_amount' => 0,
+        'financial_type_id' => $this->lookup('ContributionPage_' . $identifier, 'financial_type_id'),
+      ], 'contribution_amount_15');
+      $this->createTestEntity('PriceFieldValue', [
+        'price_field_id' => $priceField['id'],
+        'label' => 'Twenty Five',
+        'name' => 'contribution_amount_25',
+        'amount' => 25,
+        'non_deductible_amount' => 0,
+        'financial_type_id' => $this->lookup('ContributionPage_' . $identifier, 'financial_type_id'),
+      ], 'contribution_amount_25');
+      $this->createTestEntity('PriceFieldValue', [
+        'price_field_id' => $priceField['id'],
+        'label' => 'Nothing',
+        'name' => 'contribution_amount_0',
+        'amount' => 0,
+        'non_deductible_amount' => 0,
+        'financial_type_id' => $this->lookup('ContributionPage_' . $identifier, 'financial_type_id'),
+      ], 'contribution_amount_0');
+    }
+    if ($otherAmountField !== FALSE) {
+      $priceField = $this->createTestEntity('PriceField', [
+        'price_set_id' => $priceSetID,
+        'label' => 'Other Amount',
+        'html_type' => 'Text',
+        'name' => 'other_amount',
+      ], 'other_amount');
+      $this->createTestEntity('PriceFieldValue', [
+        'price_field_id' => $priceField['id'],
+        'label' => 'Other Amount',
+        'name' => 'other_amount',
+        'amount' => 1,
+        'non_deductible_amount' => 0,
+        'financial_type_id' => $this->lookup('ContributionPage_' . $identifier, 'financial_type_id'),
+      ], 'other_amount');
+    }
+  }
+
   /**
    * Add profiles to the event.
    *
@@ -339,7 +464,7 @@ trait ContributionPageTestTrait {
       'billing_first_name' => 'Dave',
       'billing_middle_name' => 'Joseph',
       'billing_last_name' => 'Wong',
-      'email' => 'dave@example.com',
+      'email-' . \CRM_Core_BAO_LocationType::getBilling() => 'dave@example.com',
       'payment_processor_id' => $this->ids['PaymentProcessor'][$processorIdentifier],
       'credit_card_number' => '4111111111111111',
       'credit_card_type' => 'Visa',
index de552cacab93b7d43ff29a8e4f1214d8d22a4951..7d25368f5abdea4cea40a22f718ef39027f06122 100644 (file)
@@ -34,6 +34,8 @@
     function clearAmountOther(otherPriceFieldName) {
       cj('#' + otherPriceFieldName).val('');
       cj('#' + otherPriceFieldName).blur();
+      // @todo - remove the next 2 lines - they seems to relate to a field that is never present
+      // as amount_other will be (e.g) price_4
       if (document.Main.amount_other == null) return; // other_amt field not present; do nothing
       document.Main.amount_other.value = "";
     }
index d87cf98967278994f8204ed4108d0070a38e521f..47e0a3abbbf6f165ae83d9b80353c44f13f87695 100644 (file)
@@ -444,7 +444,60 @@ class CRM_Contribute_Form_Contribution_ConfirmTest extends CiviUnitTestCase {
       'Tax Rate',
       'Subtotal',
     ]);
+  }
 
+  /**
+   * Test submit with a membership block in place.
+   *
+   * This test uses a quick config price set - which means line items
+   * do not show on the receipts. Separate payments are only supported
+   * with quick config.
+   *
+   * We are expecting a separate payment for the membership vs the contribution.
+   *
+   * @throws \CRM_Core_Exception
+   * @throws \Civi\API\Exception\UnauthorizedException
+   */
+  public function testSubmitMembershipBlockIsSeparatePaymentPaymentProcessorNow(): void {
+    $this->contributionPageQuickConfigCreate([], [], TRUE, TRUE, TRUE, TRUE);
+    $processor = \Civi\Payment\System::singleton()->getById($this->ids['PaymentProcessor']['dummy']);
+    $processor->setDoDirectPaymentResult(['payment_status_id' => 1, 'fee_amount' => .72]);
+    $this->submitOnlineContributionForm([
+      'payment_processor_id' => $this->ids['PaymentProcessor']['dummy'],
+      'price_' . $this->ids['PriceField']['contribution_amount'] => $this->ids['PriceFieldValue']['contribution_amount_15'],
+      'price_' . $this->ids['PriceField']['membership_amount'] => $this->ids['PriceFieldValue']['membership_general'],
+      'id' => $this->getContributionPageID(),
+    ] + $this->getBillingSubmitValues(),
+    $this->getContributionPageID());
+
+    $contributions = $this->callAPISuccess('Contribution', 'get', [
+      'contribution_page_id' => $this->getContributionPageID(),
+      'contribution_status_id' => 1,
+    ])['values'];
+    $this->assertCount(2, $contributions);
+    $membershipPayment = $this->callAPISuccess('MembershipPayment', 'getsingle', ['return' => ['contribution_id', 'membership_id']]);
+    $this->assertArrayHasKey($membershipPayment['contribution_id'], $contributions);
+    $membership = $this->callAPISuccessGetSingle('Membership', ['id' => $membershipPayment['membership_id']]);
+    $this->assertEquals($membership['contact_id'], $contributions[$membershipPayment['contribution_id']]['contact_id']);
+    $lineItem = $this->callAPISuccessGetSingle('LineItem', ['entity_table' => 'civicrm_membership']);
+    $this->assertEquals($membership['id'], $lineItem['entity_id']);
+    $this->assertEquals($membershipPayment['contribution_id'], $lineItem['contribution_id']);
+    $this->assertEquals(1, $lineItem['qty']);
+    $this->assertEquals(100, $lineItem['unit_price']);
+    $this->assertEquals(100, $lineItem['line_total']);
+    foreach ($contributions as $contribution) {
+      $this->assertEquals(.72, $contribution['fee_amount']);
+      $this->assertEquals($contribution['total_amount'] - .72, $contribution['net_amount']);
+    }
+    $this->assertMailSentContainingStrings(['$15.00', 'Contribution Information'], 0);
+    $this->assertMailSentContainingStrings([
+      'Membership Information',
+      'Membership Type',
+      'General',
+      'Membership Start Date',
+      'Membership Fee',
+      '$100',
+    ], 1);
   }
 
 }
index b18d45a6ad14c17de2a08581922cd2f865d06484..557152140cb0d354b1fbe5652ee85381207a5b60 100644 (file)
@@ -544,52 +544,6 @@ class api_v3_ContributionPageTest extends CiviUnitTestCase {
     $this->assertEquals($membership['contact_id'], $contributions[1]['contact_id']);
   }
 
-  /**
-   * Test submit with a membership block in place.
-   *
-   * This test uses a quick config price set - which means line items
-   * do not show on the receipts. Separate payments are only supported
-   * with quick config.
-   *
-   * We are expecting a separate payment for the membership vs the contribution.
-   *
-   * @throws \CRM_Core_Exception
-   * @throws \Civi\API\Exception\UnauthorizedException
-   */
-  public function testSubmitMembershipBlockIsSeparatePaymentPaymentProcessorNow(): void {
-    $mut = new CiviMailUtils($this, TRUE);
-    $this->setUpMembershipContributionPage(TRUE);
-    $processor = Civi\Payment\System::singleton()->getById($this->ids['PaymentProcessor']['dummy']);
-    $processor->setDoDirectPaymentResult(['payment_status_id' => 1, 'fee_amount' => .72]);
-    $submitParams = $this->getSubmitParamsContributionPlusMembership(TRUE);
-
-    $this->callAPISuccess('ContributionPage', 'submit', $submitParams);
-    $contributions = $this->callAPISuccess('Contribution', 'get', [
-      'contribution_page_id' => $this->getContributionPageID(),
-      'contribution_status_id' => 1,
-    ]);
-    $this->assertCount(2, $contributions['values']);
-    $membershipPayment = $this->callAPISuccess('MembershipPayment', 'getsingle', ['return' => ['contribution_id', 'membership_id']]);
-    $this->assertArrayHasKey($membershipPayment['contribution_id'], $contributions['values']);
-    $membership = $this->callAPISuccessGetSingle('Membership', ['id' => $membershipPayment['membership_id']]);
-    $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
-    $lineItem = $this->callAPISuccessGetSingle('LineItem', ['entity_table' => 'civicrm_membership']);
-    $this->assertEquals($membership['id'], $lineItem['entity_id']);
-    $this->assertEquals($membershipPayment['contribution_id'], $lineItem['contribution_id']);
-    $this->assertEquals(1, $lineItem['qty']);
-    $this->assertEquals(2, $lineItem['unit_price']);
-    $this->assertEquals(2, $lineItem['line_total']);
-    foreach ($contributions['values'] as $contribution) {
-      $this->assertEquals(.72, $contribution['fee_amount']);
-      $this->assertEquals($contribution['total_amount'] - .72, $contribution['net_amount']);
-    }
-    // The total string is currently absent & it seems worse with - although at some point
-    // it may have been intended
-    $mut->checkAllMailLog(['$2.00', 'Contribution Information', '$88.00'], ['Total:']);
-    $mut->stop();
-    $mut->clearMessages();
-  }
-
   /**
    * Test submit with a membership block in place.
    *