protected function processMembership($membershipParams, $contactID, $customFieldsFormatted, $premiumParams,
$membershipLineItems): void {
- $membershipTypeIDs = (array) $membershipParams['selectMembership'];
+ $membershipTypeIDs = array_keys($this->order->getMembershipTypes());
$membershipTypes = CRM_Member_BAO_Membership::buildMembershipTypeValues($this, $membershipTypeIDs);
$membershipType = empty($membershipTypes) ? [] : reset($membershipTypes);
$priceFieldValue = $this->_params["price_{$priceFieldId}"];
}
$selectedMembershipTypeID = $this->_values['fee'][$priceFieldId]['options'][$priceFieldValue]['membership_type_id'] ?? NULL;
- if (!$selectedMembershipTypeID) {
+ if (!$selectedMembershipTypeID || !$this->getPaymentProcessorValue('is_recur')) {
return;
}
// Check if membership the selected membership is automatically opted into auto renew or give user the option.
// In the 2nd case we check that the user has in deed opted in (auto renew as at June 22 is the field name for the membership auto renew checkbox)
// Also check that the payment Processor used can support recurring contributions.
- $membershipTypes = CRM_Price_BAO_PriceSet::getMembershipTypesFromPriceSet($this->getPriceSetID());
- if (in_array($selectedMembershipTypeID, $membershipTypes['autorenew_required'])
- || (in_array($selectedMembershipTypeID, $membershipTypes['autorenew_optional']) &&
- !empty($this->_params['auto_renew']))
- && !empty($this->_paymentProcessor['is_recur'])
+ $membershipTypeDetails = CRM_Member_BAO_MembershipType::getMembershipType($selectedMembershipTypeID);
+ if (
+ // 2 means required
+ $membershipTypeDetails['auto_renew'] === 2
+ // 1 means optional - so they must also select the check box on the form
+ || ($membershipTypeDetails['auto_renew'] === 1 && !empty($this->_params['auto_renew']))
) {
$this->_params['auto_renew'] = TRUE;
$this->_params['is_recur'] = $this->_values['is_recur'] = 1;
- $membershipTypeDetails = \Civi\Api4\MembershipType::get(FALSE)
- ->addWhere('id', '=', $selectedMembershipTypeID)
- ->execute()
- ->first();
- $this->_params['frequency_interval'] = $this->_params['frequency_interval'] ?? $this->_values['fee'][$priceFieldId]['options'][$priceFieldValue]['membership_num_terms'];
+ // If membership_num_terms is not specified on the the price field value (which seems not uncommon
+ // in default config) then the membership type provides the values.
+ // @todo - access the line item value from $this->getLineItems() rather than _values['fee']
+ $this->_params['frequency_interval'] = $this->getSubmittedValue('frequency_interval') ?? ($this->_values['fee'][$priceFieldId]['options'][$priceFieldValue]['membership_num_terms'] ?? ($membershipTypeDetails['duration_interval'] ?? 1));
$this->_params['frequency_unit'] = $this->_params['frequency_unit'] ?? $membershipTypeDetails['duration_unit'];
}
- elseif (!$this->_separateMembershipPayment && (in_array($selectedMembershipTypeID, $membershipTypes['autorenew_required'])
- || in_array($selectedMembershipTypeID, $membershipTypes['autorenew_optional']))) {
+ // This seems like it repeats the above with less care...
+ elseif (!$this->_separateMembershipPayment && ($membershipTypeDetails['auto_renew'] === 2
+ || $membershipTypeDetails['auto_renew'] === 1)) {
// otherwise check if we have a separate membership payment setting as that will allow people to independently opt into recurring contributions and memberships
// If we don't have that and the membership type is auto recur or opt into recur set is_recur to 0.
$this->_params['is_recur'] = $this->_values['is_recur'] = 0;
$lineItem['membership_type_id'] = $lineItem['membership_type_id'] ?? NULL;
if ($lineItem['membership_type_id']) {
$lineItem['entity_table'] = 'civicrm_membership';
+ $lineItem['membership_num_terms'] = $lineItem['membership_num_terms'] ?:1;
}
$lineItem['title'] = $this->getLineItemTitle($lineItem);
$lineItem['tax_rate'] = $taxRate = $this->getTaxRate((int) $lineItem['financial_type_id']);
{if !empty($auto_renew)} {* Auto-renew membership confirmation *}
{crmRegion name="contribution-thankyou-recur-membership"}
<br />
- {if $installments > 1}
+ {if !$installments || $installments > 1}
{if $frequency_interval > 1}
<strong>{ts 1=$frequency_interval 2=$frequency_unit}This membership will be renewed automatically every %1 %2(s).{/ts}</strong>
{else}
use Civi\Api4\Contribution;
use Civi\Api4\LineItem;
+use Civi\Api4\PriceSet;
use Civi\Api4\PriceSetEntity;
use Civi\Test\ContributionPageTestTrait;
use Civi\Test\FormTrait;
$this->assertEquals(5, $recurringContribution['contribution_status_id']);
}
+ /**
+ * Helper function to set up contribution page which can be used to purchase a
+ * membership type for different intervals.
+ */
+ public function setUpMultiIntervalMembershipContributionPage(): void {
+ // These all have auto_renew set to 2 - ie require auto-renew.
+ $this->membershipTypeCreate([
+ 'name' => 'monthly',
+ 'auto_renew' => 2,
+ 'duration_unit' => 'month',
+ 'minimum_fee' => 10,
+ ], 'monthly');
+
+ $this->membershipTypeCreate([
+ 'name' => 'bi_monthly',
+ 'auto_renew' => 2,
+ 'duration_unit' => 'month',
+ 'duration_interval' => 2,
+ 'minimum_fee' => 79,
+ ], 'bi_monthly');
+
+ $this->membershipTypeCreate([
+ 'auto_renew' => 2,
+ 'name' => 'yearly',
+ 'duration_unit' => 'year',
+ 'minimum_fee' => 100,
+ ], 'yearly');
+ $this->contributionPageQuickConfigCreate([], [], FALSE);
+ }
+
+ /**
+ * Test submit with a membership block in place.
+ *
+ * @dataProvider getBooleanDataProvider
+ *
+ * @param bool $isQuickConfig
+ */
+ public function testSubmitMultiIntervalMembershipContributionPage(bool $isQuickConfig): void {
+ $this->setUpMultiIntervalMembershipContributionPage();
+ PriceSet::update()->setValues(['is_quick_config' => $isQuickConfig])->addWhere('id', '=', $this->ids['PriceSet']['QuickConfig'])->execute();
+ if (!$isQuickConfig) {
+ $this->createTestEntity('PriceFieldValue', [
+ 'name' => 'CRM-21177_12_Months',
+ 'label' => 'CRM-21177 - 12 Months',
+ 'amount' => 200,
+ 'membership_num_terms' => 12,
+ 'membership_type_id' => $this->ids['MembershipType']['monthly'],
+ 'price_field_id' => $this->ids['PriceField']['membership_amount'],
+ 'financial_type_id:name' => 'Member Dues',
+ ], 'membership_12_months');
+ }
+ $submitParams = [
+ 'price_' . $this->ids['PriceField']['membership_amount'] => $this->ids['PriceFieldValue']['membership_monthly'],
+ 'first_name' => 'Billy',
+ 'last_name' => 'Gruff',
+ 'email' => 'billy@goat.gruff',
+ 'payment_processor_id' => $this->ids['PaymentProcessor']['dummy'],
+ 'credit_card_number' => '4111111111111111',
+ 'credit_card_type' => 'Visa',
+ 'credit_card_exp_date' => ['M' => 9, 'Y' => 2040],
+ 'cvv2' => 123,
+ ];
+ $this->submitOnlineContributionForm($submitParams,
+ $this->getContributionPageID());
+ $membership = $this->callAPISuccessGetSingle('Membership', []);
+ $contributionRecur = $this->callAPISuccessGetSingle('ContributionRecur', ['id' => $membership['contribution_recur_id']]);
+ $this->assertEquals('month', $contributionRecur['frequency_unit']);
+ $this->assertEquals(1, $contributionRecur['frequency_interval']);
+
+ $submitParams['price_' . $this->ids['PriceField']['membership_amount']] = $this->ids['PriceFieldValue']['membership_yearly'];
+ $this->submitOnlineContributionForm($submitParams,
+ $this->getContributionPageID());
+ $membership = $this->callAPISuccessGetSingle('Membership', ['membership_type_id' => $this->ids['MembershipType']['yearly']]);
+ $contributionRecur = $this->callAPISuccessGetSingle('ContributionRecur', ['id' => $membership['contribution_recur_id']]);
+ $this->assertEquals('year', $contributionRecur['frequency_unit']);
+ $this->assertEquals(1, $contributionRecur['frequency_interval']);
+
+ if (!$isQuickConfig) {
+ $submitParams['price_' . $this->ids['PriceField']['membership_amount']] = $this->ids['PriceFieldValue']['membership_12_months'];
+ $this->submitOnlineContributionForm($submitParams,
+ $this->getContributionPageID());
+ $contribution = $this->callAPISuccess('Contribution', 'get', [
+ 'contribution_page_id' => $this->getContributionPageID(),
+ 'sequential' => 1,
+ 'api.ContributionRecur.getsingle' => [],
+ 'version' => 3,
+ ]);
+ $this->assertEquals(1, $contribution['values'][0]['api.ContributionRecur.getsingle']['frequency_interval']);
+ $this->assertEquals(1, $contribution['values'][1]['api.ContributionRecur.getsingle']['frequency_interval']);
+ $this->assertEquals(12, $contribution['values'][2]['api.ContributionRecur.getsingle']['frequency_interval']);
+
+ $this->assertEquals('month', $contribution['values'][0]['api.ContributionRecur.getsingle']['frequency_unit']);
+ $this->assertEquals('year', $contribution['values'][1]['api.ContributionRecur.getsingle']['frequency_unit']);
+ $this->assertEquals('month', $contribution['values'][2]['api.ContributionRecur.getsingle']['frequency_unit']);
+ }
+
+ $submitParams['price_' . $this->ids['PriceField']['membership_amount']] = $this->ids['PriceFieldValue']['membership_bi_monthly'];
+ $this->submitOnlineContributionForm($submitParams,
+ $this->getContributionPageID());
+ $membership = $this->callAPISuccessGetSingle('Membership', ['membership_type_id' => $this->ids['MembershipType']['bi_monthly']]);
+ $contributionRecur = $this->callAPISuccessGetSingle('ContributionRecur', ['id' => $membership['contribution_recur_id']]);
+ $this->assertEquals('month', $contributionRecur['frequency_unit']);
+ $this->assertEquals(2, $contributionRecur['frequency_interval']);
+ }
+
}
}
}
- /**
- * Helper function to set up contribution page which can be used to purchase a
- * membership type for different intervals.
- */
- public function setUpMultiIntervalMembershipContributionPage(): void {
- $this->setupPaymentProcessor();
- $contributionPage = $this->callAPISuccess('ContributionPage', 'create', $this->params);
- $this->ids['ContributionPage']['Membership'] = $contributionPage['id'];
-
- $this->ids['MembershipTypeMonth'] = $this->membershipTypeCreate([
- // force auto-renew
- 'auto_renew' => 2,
- 'duration_unit' => 'month',
- ]);
-
- $this->ids['MembershipTypeYear'] = $this->membershipTypeCreate([
- // force auto-renew
- 'auto_renew' => 2,
- 'duration_unit' => 'year',
- ]);
-
- $priceSet = $this->callAPISuccess('PriceSet', 'create', [
- 'is_quick_config' => 0,
- 'extends' => 'CiviMember',
- 'financial_type_id' => 'Member Dues',
- 'title' => 'CRM-21177',
- ]);
- $this->_ids['price_set'] = $priceSet['id'];
-
- $priceField = $this->callAPISuccess('price_field', 'create', [
- 'price_set_id' => $this->_ids['price_set'],
- 'name' => 'membership_type',
- 'label' => 'Membership Type',
- 'html_type' => 'Radio',
- ]);
- $this->_ids['price_field'] = $priceField['id'];
-
- $priceFieldValueMonthly = $this->callAPISuccess('price_field_value', 'create', [
- 'name' => 'CRM-21177_Monthly',
- 'label' => 'CRM-21177 - Monthly',
- 'amount' => 20,
- 'membership_num_terms' => 1,
- 'membership_type_id' => $this->ids['MembershipTypeMonth'],
- 'price_field_id' => $this->_ids['price_field'],
- 'financial_type_id' => 'Member Dues',
- ]);
- $this->_ids['price_field_value_monthly'] = $priceFieldValueMonthly['id'];
-
- $priceFieldValue12Months = $this->callAPISuccess('price_field_value', 'create', [
- 'name' => 'CRM-21177_12_Months',
- 'label' => 'CRM-21177 - 12 Months',
- 'amount' => 200,
- 'membership_num_terms' => 12,
- 'membership_type_id' => $this->ids['MembershipTypeMonth'],
- 'price_field_id' => $this->_ids['price_field'],
- 'financial_type_id' => 'Member Dues',
- ]);
- $this->_ids['price_field_value_12_months'] = $priceFieldValue12Months['id'];
-
- $priceFieldValueYearly = $this->callAPISuccess('price_field_value', 'create', [
- 'name' => 'CRM-21177_Yearly',
- 'label' => 'CRM-21177 - Yearly',
- 'amount' => 200,
- 'membership_num_terms' => 1,
- 'membership_type_id' => $this->ids['MembershipTypeYear'],
- 'price_field_id' => $this->_ids['price_field'],
- 'financial_type_id' => 'Member Dues',
- ]);
- $this->_ids['price_field_value_yearly'] = $priceFieldValueYearly['id'];
-
- CRM_Price_BAO_PriceSet::addTo('civicrm_contribution_page', $this->getContributionPageID(), $this->_ids['price_set']);
-
- $this->callAPISuccess('membership_block', 'create', [
- 'entity_id' => $this->getContributionPageID(),
- 'entity_table' => 'civicrm_contribution_page',
- 'is_required' => TRUE,
- 'is_separate_payment' => FALSE,
- 'is_active' => TRUE,
- 'membership_type_default' => $this->ids['MembershipTypeMonth'],
- ]);
- }
-
- /**
- * Test submit with a membership block in place.
- */
- public function testSubmitMultiIntervalMembershipContributionPage(): void {
- $this->setUpMultiIntervalMembershipContributionPage();
- $submitParams = [
- 'price_' . $this->_ids['price_field'] => $this->_ids['price_field_value_monthly'],
- 'id' => $this->getContributionPageID(),
- 'amount' => 20,
- 'first_name' => 'Billy',
- 'last_name' => 'Gruff',
- 'email' => 'billy@goat.gruff',
- 'payment_processor_id' => $this->ids['PaymentProcessor']['dummy'],
- 'credit_card_number' => '4111111111111111',
- 'credit_card_type' => 'Visa',
- 'credit_card_exp_date' => ['M' => 9, 'Y' => 2040],
- 'cvv2' => 123,
- 'auto_renew' => 1,
- ];
- $this->callAPISuccess('contribution_page', 'submit', $submitParams);
-
- $submitParams['price_' . $this->_ids['price_field']] = $this->_ids['price_field_value_yearly'];
- $this->callAPISuccess('contribution_page', 'submit', $submitParams);
-
- $submitParams['price_' . $this->_ids['price_field']] = $this->_ids['price_field_value_12_months'];
- $this->callAPISuccess('contribution_page', 'submit', $submitParams);
-
- $contribution = $this->callAPISuccess('Contribution', 'get', [
- 'contribution_page_id' => $this->getContributionPageID(),
- 'sequential' => 1,
- 'api.ContributionRecur.getsingle' => [],
- ]);
- $this->assertEquals(1, $contribution['values'][0]['api.ContributionRecur.getsingle']['frequency_interval']);
- $this->assertEquals(1, $contribution['values'][1]['api.ContributionRecur.getsingle']['frequency_interval']);
- $this->assertEquals(12, $contribution['values'][2]['api.ContributionRecur.getsingle']['frequency_interval']);
-
- $this->assertEquals('month', $contribution['values'][0]['api.ContributionRecur.getsingle']['frequency_unit']);
- $this->assertEquals('year', $contribution['values'][1]['api.ContributionRecur.getsingle']['frequency_unit']);
- $this->assertEquals('month', $contribution['values'][2]['api.ContributionRecur.getsingle']['frequency_unit']);
- }
-
/**
* Create a payment processor instance.
*/