From a5f761a438a3119f89fca0705077c81dd5c0a436 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Sat, 18 Nov 2023 19:23:32 +1300 Subject: [PATCH] Improve test cover for full form flow of separate payment --- CRM/Contribute/Form/Contribution/Confirm.php | 5 +- CRM/Contribute/Form/Contribution/Main.php | 2 +- Civi/Test/ContributionPageTestTrait.php | 127 +++++++++++++++++- .../CRM/Contribute/Form/Contribution/Main.tpl | 2 + .../Form/Contribution/ConfirmTest.php | 53 ++++++++ tests/phpunit/api/v3/ContributionPageTest.php | 46 ------- 6 files changed, 184 insertions(+), 51 deletions(-) diff --git a/CRM/Contribute/Form/Contribution/Confirm.php b/CRM/Contribute/Form/Contribution/Confirm.php index 3b6df20e28..d467807093 100644 --- a/CRM/Contribute/Form/Contribution/Confirm.php +++ b/CRM/Contribute/Form/Contribution/Confirm.php @@ -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 diff --git a/CRM/Contribute/Form/Contribution/Main.php b/CRM/Contribute/Form/Contribution/Main.php index 327cecf8d9..0b2ee65d92 100644 --- a/CRM/Contribute/Form/Contribution/Main.php +++ b/CRM/Contribute/Form/Contribution/Main.php @@ -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(); diff --git a/Civi/Test/ContributionPageTestTrait.php b/Civi/Test/ContributionPageTestTrait.php index 19dd23166a..7d6a0d1cf6 100644 --- a/Civi/Test/ContributionPageTestTrait.php +++ b/Civi/Test/ContributionPageTestTrait.php @@ -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', diff --git a/templates/CRM/Contribute/Form/Contribution/Main.tpl b/templates/CRM/Contribute/Form/Contribution/Main.tpl index de552cacab..7d25368f5a 100644 --- a/templates/CRM/Contribute/Form/Contribution/Main.tpl +++ b/templates/CRM/Contribute/Form/Contribution/Main.tpl @@ -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 = ""; } diff --git a/tests/phpunit/CRM/Contribute/Form/Contribution/ConfirmTest.php b/tests/phpunit/CRM/Contribute/Form/Contribution/ConfirmTest.php index d87cf98967..47e0a3abbb 100644 --- a/tests/phpunit/CRM/Contribute/Form/Contribution/ConfirmTest.php +++ b/tests/phpunit/CRM/Contribute/Form/Contribution/ConfirmTest.php @@ -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); } } diff --git a/tests/phpunit/api/v3/ContributionPageTest.php b/tests/phpunit/api/v3/ContributionPageTest.php index b18d45a6ad..557152140c 100644 --- a/tests/phpunit/api/v3/ContributionPageTest.php +++ b/tests/phpunit/api/v3/ContributionPageTest.php @@ -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. * -- 2.25.1