From 268a84f2c4ea6868fb7d2ddd242dd13fab6c0e41 Mon Sep 17 00:00:00 2001 From: "deb.monish" Date: Wed, 17 May 2017 18:39:32 +0530 Subject: [PATCH] CRM-20321: Changing membership type should change related contribution --- CRM/Admin/Form/Preferences/Contribute.php | 4 + CRM/Contribute/BAO/Contribution.php | 5 + CRM/Member/Form/Membership.php | 81 ++++++++++++++++ api/v3/examples/Setting/GetFields.php | 15 +++ settings/Contribute.setting.php | 15 +++ .../CRM/Member/Form/MembershipTest.php | 95 +++++++++++++++++++ 6 files changed, 215 insertions(+) diff --git a/CRM/Admin/Form/Preferences/Contribute.php b/CRM/Admin/Form/Preferences/Contribute.php index 9bb789b6bf..9fb2377ccc 100644 --- a/CRM/Admin/Form/Preferences/Contribute.php +++ b/CRM/Admin/Form/Preferences/Contribute.php @@ -37,6 +37,7 @@ class CRM_Admin_Form_Preferences_Contribute extends CRM_Admin_Form_Preferences { protected $_settings = array( 'cvv_backoffice_required' => CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, + 'update_contribution_on_membership_type_change' => CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'acl_financial_type' => CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'always_post_to_accounts_receivable' => CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'deferred_revenue_enabled' => CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, @@ -196,6 +197,9 @@ class CRM_Admin_Form_Preferences_Contribute extends CRM_Admin_Form_Preferences { unset($params['qfKey']); unset($params['entryURL']); Civi::settings()->set('contribution_invoice_settings', $params); + Civi::settings()->set('update_contribution_on_membership_type_change', + CRM_Utils_Array::value('update_contribution_on_membership_type_change', $params) + ); // to set default value for 'Invoices / Credit Notes' checkbox on display preferences $values = CRM_Core_BAO_Setting::getItem("CiviCRM Preferences"); diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index 01a4f4283d..3441c7ee57 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -4088,6 +4088,11 @@ WHERE eft.financial_trxn_id IN ({$trxnId}, {$baseTrxnId['financialTrxnId']}) } } } + elseif ($component == 'membership') { + $entity = $component; + $entityTable = 'civicrm_membership'; + $contributionId = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipPayment', $id, 'contribution_id', 'membership_id'); + } else { $contributionId = $id; $entity = 'contribution'; diff --git a/CRM/Member/Form/Membership.php b/CRM/Member/Form/Membership.php index b21338e75f..163147f228 100644 --- a/CRM/Member/Form/Membership.php +++ b/CRM/Member/Form/Membership.php @@ -1725,10 +1725,91 @@ class CRM_Member_Form_Membership extends CRM_Member_Form { } $isRecur = CRM_Utils_Array::value('is_recur', $params); + $this->updateContributionOnMembershipTypeChange($params, $membership); $this->setStatusMessage($membership, $endDate, $receiptSent, $membershipTypes, $createdMemberships, $isRecur, $calcDates, $mailSend); return $createdMemberships; } + /** + * Update related contribution of a membership if update_contribution_on_membership_type_change + * contribution setting is enabled and type is changed on edit + * + * @param array $inputParams + * submitted form values + * @param CRM_Member_DAO_Membership $membership + * Updated membership object + * + */ + protected function updateContributionOnMembershipTypeChange($inputParams, $membership) { + if (Civi::settings()->get('update_contribution_on_membership_type_change') && + ($this->_action & CRM_Core_Action::UPDATE) && // on update + $this->_id && // if ID is present + !in_array($this->_memType, $this->_memTypeSelected) // if selected membership doesn't match with earlier membership + ) { + if (CRM_Utils_Array::value('is_recur', $inputParams)) { + CRM_Core_Session::setStatus(ts('Associated recurring contribution cannot be updated on membership type change.', ts('Error'), 'error')); + return; + } + + // fetch lineitems by updated membership ID + $lineItems = CRM_Price_BAO_LineItem::getLineItems($membership->id, 'membership'); + // retrieve the related contribution ID + $contributionID = CRM_Core_DAO::getFieldValue( + 'CRM_Member_DAO_MembershipPayment', + $membership->id, + 'contribution_id', + 'membership_id' + ); + // get price fields of chosen price-set + $priceSetDetails = CRM_Utils_Array::value( + $this->_priceSetId, + CRM_Price_BAO_PriceSet::getSetDetail( + $this->_priceSetId, + TRUE, + TRUE + ) + ); + + // add price field information in $inputParams + self::addPriceFieldByMembershipType($inputParams, $priceSetDetails['fields'], $membership->membership_type_id); + // paid amount + $paidAmount = CRM_Utils_Array::value('paid', CRM_Contribute_BAO_Contribution::getPaymentInfo($membership->id, 'membership')); + // update related contribution and financial records + CRM_Price_BAO_LineItem::changeFeeSelections( + $inputParams, + $membership->id, + 'membership', + $contributionID, + $priceSetDetails['fields'], + $lineItems, $paidAmount + ); + CRM_Core_Session::setStatus(ts('Associated contribution is updated on membership type change.'), ts('Success'), 'success'); + } + } + + /** + * Add selected price field information in $formValues + * + * @param array $formValues + * submitted form values + * @param array $priceFields + * Price fields of selected Priceset ID + * @param int $membershipTypeID + * Selected membership type ID + * + */ + public static function addPriceFieldByMembershipType(&$formValues, $priceFields, $membershipTypeID) { + foreach ($priceFields as $priceFieldID => $priceField) { + if (isset($priceField['options']) && count($priceField['options'])) { + foreach ($priceField['options'] as $option) { + if ($option['membership_type_id'] == $membershipTypeID) { + $formValues["price_{$priceFieldID}"] = $option['id']; + break; + } + } + } + } + } /** * Set context in session. */ diff --git a/api/v3/examples/Setting/GetFields.php b/api/v3/examples/Setting/GetFields.php index 0da8b46d79..739f9d4814 100644 --- a/api/v3/examples/Setting/GetFields.php +++ b/api/v3/examples/Setting/GetFields.php @@ -240,6 +240,21 @@ function setting_getfields_expectedresult() { 'description' => '', 'help_text' => '', ), + 'update_contribution_on_membership_type_change' => array( + 'group_name' => 'Contribute Preferences', + 'group' => 'contribute', + 'name' => 'update_contribution_on_membership_type_change', + 'type' => 'Integer', + 'html_type' => 'checkbox', + 'quick_form_type' => 'Element', + 'default' => 0, + 'add' => '4.7', + 'title' => 'Automatically update related contributions when Membership Type is changed', + 'is_domain' => 1, + 'is_contact' => 0, + 'description' => 'Enabling this setting will update related contribution of membership(s) except if the membership is paid for with a recurring contribution.', + 'help_text' => '', + ), 'contact_view_options' => array( 'group_name' => 'CiviCRM Preferences', 'group' => 'core', diff --git a/settings/Contribute.setting.php b/settings/Contribute.setting.php index 67313e3031..a9b5109988 100644 --- a/settings/Contribute.setting.php +++ b/settings/Contribute.setting.php @@ -150,4 +150,19 @@ return array( 'description' => NULL, 'help_text' => NULL, ), + 'update_contribution_on_membership_type_change' => array( + 'group_name' => 'Contribute Preferences', + 'group' => 'contribute', + 'name' => 'update_contribution_on_membership_type_change', + 'type' => 'Integer', + 'html_type' => 'checkbox', + 'quick_form_type' => 'Element', + 'default' => 0, + 'add' => '4.7', + 'title' => 'Automatically update related contributions when Membership Type is changed', + 'is_domain' => 1, + 'is_contact' => 0, + 'description' => 'Enabling this setting will update related contribution of membership(s) except if the membership is paid for with a recurring contribution.', + 'help_text' => NULL, + ), ); diff --git a/tests/phpunit/CRM/Member/Form/MembershipTest.php b/tests/phpunit/CRM/Member/Form/MembershipTest.php index 0c2c1dee82..09476038bc 100644 --- a/tests/phpunit/CRM/Member/Form/MembershipTest.php +++ b/tests/phpunit/CRM/Member/Form/MembershipTest.php @@ -110,6 +110,7 @@ class CRM_Member_Form_MembershipTest extends CiviUnitTestCase { 'name' => "AnnualFixed", 'member_of_contact_id' => 23, 'duration_unit' => "year", + 'minimum_fee' => 50, 'duration_interval' => 1, 'period_type' => "fixed", 'fixed_period_start_day' => "101", @@ -512,6 +513,100 @@ class CRM_Member_Form_MembershipTest extends CiviUnitTestCase { $this->mut->stop(); } + /** + * Test the submit function of the membership form on membership type change. + * Check if the related contribuion is also updated if the minimum_fee didn't match + */ + public function testContributionUpdateOnMembershipTypeChange() { + // Step 1: Create a Membership via backoffice whose with 50.00 payment + $form = $this->getForm(); + $form->preProcess(); + $this->mut = new CiviMailUtils($this, TRUE); + $this->createLoggedInUser(); + $priceSet = $this->callAPISuccess('PriceSet', 'Get', array("extends" => "CiviMember")); + $form->set('priceSetId', $priceSet['id']); + CRM_Price_BAO_PriceSet::buildPriceSet($form); + $params = array( + 'cid' => $this->_individualId, + 'join_date' => date('m/d/Y', time()), + 'start_date' => '', + 'end_date' => '', + // This format reflects the 23 being the organisation & the 25 being the type. + 'membership_type_id' => array(23, $this->membershipTypeAnnualFixedID), + 'record_contribution' => 1, + 'total_amount' => 50, + 'receive_date' => date('m/d/Y', time()), + 'receive_date_time' => '08:36PM', + 'payment_instrument_id' => array_search('Check', $this->paymentInstruments), + 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'), + 'financial_type_id' => '2', //Member dues, see data.xml + 'payment_processor_id' => $this->_paymentProcessorID, + ); + $form->_contactID = $this->_individualId; + $form->testSubmit($params); + $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId)); + // check the membership status after partial payment, if its Pending + $this->assertEquals(array_search('New', CRM_Member_PseudoConstant::membershipStatus()), $membership['status_id']); + $contribution = $this->callAPISuccessGetSingle('Contribution', array( + 'contact_id' => $this->_individualId, + )); + $this->assertEquals('Completed', $contribution['contribution_status']); + $this->assertEquals(50.00, $contribution['total_amount']); + $this->assertEquals(50.00, $contribution['net_amount']); + + // Step 2: Change the membership type whose minimum free is less than earlier membership + $secondMembershipType = $this->callAPISuccess('membership_type', 'create', array( + 'domain_id' => 1, + 'name' => "Second Test Membership", + 'member_of_contact_id' => 23, + 'duration_unit' => "month", + 'minimum_fee' => 25, + 'duration_interval' => 1, + 'period_type' => "fixed", + 'fixed_period_start_day' => "101", + 'fixed_period_rollover_day' => "1231", + 'relationship_type_id' => 20, + 'financial_type_id' => 2, + )); + Civi::settings()->set('update_contribution_on_membership_type_change', TRUE); + $form = $this->getForm(); + $form->preProcess(); + $form->_id = $membership['id']; + $form->set('priceSetId', $priceSet['id']); + CRM_Price_BAO_PriceSet::buildPriceSet($form); + $form->_action = CRM_Core_Action::UPDATE; + $params = array( + 'cid' => $this->_individualId, + 'join_date' => date('m/d/Y', time()), + 'start_date' => '', + 'end_date' => '', + // This format reflects the 23 being the organisation & the 25 being the type. + 'membership_type_id' => array(23, $secondMembershipType['id']), + 'record_contribution' => 1, + 'status_id' => 1, + 'total_amount' => 25, + 'receive_date' => date('m/d/Y', time()), + 'receive_date_time' => '08:36PM', + 'payment_instrument_id' => array_search('Check', $this->paymentInstruments), + 'financial_type_id' => '2', //Member dues, see data.xml + 'payment_processor_id' => $this->_paymentProcessorID, + ); + $form->_contactID = $this->_individualId; + $form->testSubmit($params); + $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId)); + // check the membership status after partial payment, if its Pending + $contribution = $this->callAPISuccessGetSingle('Contribution', array( + 'contact_id' => $this->_individualId, + )); + $payment = CRM_Contribute_BAO_Contribution::getPaymentInfo($membership['id'], 'membership', FALSE, TRUE); + // Check the contribution status on membership type change whose minimum fee was less than earlier memebership + $this->assertEquals('Pending refund', $contribution['contribution_status']); + // Earlier paid amount + $this->assertEquals(50, $payment['paid']); + // balance remaning + $this->assertEquals(-25, $payment['balance']); + } + /** * Test the submit function of the membership form. */ -- 2.25.1