-
-
Notifications
You must be signed in to change notification settings - Fork 825
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP CRM-21682 automatic membership renewal cleanup #11556
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3930,6 +3930,7 @@ public static function recordAdditionalPayment($contributionId, $trxnsData, $pay | |
} | ||
} | ||
|
||
// update membership details | ||
self::updateMembershipBasedOnCompletionOfContribution( | ||
$contributionDAO, | ||
$contributionId, | ||
|
@@ -5406,99 +5407,86 @@ protected static function isPaymentInstrumentChange(&$params, $pendingStatuses) | |
* @param int $primaryContributionID | ||
* @param string $changeDate | ||
* | ||
* @throws \CRM_Core_Exception | ||
* @throws \CiviCRM_API3_Exception | ||
*/ | ||
protected static function updateMembershipBasedOnCompletionOfContribution($contribution, $primaryContributionID, $changeDate) { | ||
$contribution->loadRelatedMembershipObjects(); | ||
if (empty($contribution->_relatedObjects['membership'])) { | ||
return; | ||
// Load memberships from contribution info. There may be multiple memberships (or none) | ||
$membershipFields = array('id', 'contact_id', 'is_test', 'membership_type_id', 'contribution_recur_id', 'status_id.is_current_member', | ||
'start_date', 'join_date', 'end_date', 'status_id'); | ||
$memberships = array(); | ||
if (!empty($contribution->contribution_recur_id)) { | ||
// Load memberships associated with recurring contribution | ||
$membershipResult = civicrm_api3('Membership', 'get', array( | ||
'contribution_recur_id' => $contribution->contribution_recur_id, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is unlikely to have more than 25 memberships associated with the same contribution, but still would be better to add 'limit' => 0 option here. same for all the API calls below. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
'return' => $membershipFields, | ||
'options' => ['limit' => 0], | ||
)); | ||
} | ||
$memberships = $contribution->_relatedObjects['membership']; | ||
foreach ($memberships as $membershipTypeIdKey => $membership) { | ||
elseif (!empty($primaryContributionID)) { | ||
// Load membership associated with original contribution | ||
$membershipPaymentResult = civicrm_api3('MembershipPayment', 'get', array( | ||
'contribution_id' => $primaryContributionID, | ||
'options' => ['limit' => 0], | ||
)); | ||
if (!empty($membershipPaymentResult['count'])) { | ||
foreach ($membershipPaymentResult['values'] as $payment) { | ||
$membershipIDs[] = $payment['membership_id']; | ||
} | ||
$membershipResult = civicrm_api3('Membership', 'get', array( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmmm, why not just get the memberships using $primaryContributionID directly instead of fetching the payments IDs from the payments first ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @omarabuhussein We are working with a single contribution at this point. Membership.get requires a recurring contribution ID which we don't have. Am I missing something? |
||
'id' => array('IN' => $membershipIDs), | ||
'return' => $membershipFields, | ||
'options' => ['limit' => 0], | ||
)); | ||
} | ||
} | ||
if (isset($membershipResult) && !empty($membershipResult['count'])) { | ||
$memberships = $membershipResult['values']; | ||
} | ||
|
||
// Loop through all found memberships and update status/renew | ||
foreach ($memberships as $membershipId => $membership) { | ||
if ($membership) { | ||
if ((!empty($contribution->contribution_recur_id)) | ||
&& (!CRM_Member_BAO_Membership::isRecurFrequencyEqualToMembershipType($membership['membership_type_id'], $membership['contribution_recur_id']))) { | ||
Civi::log()->warning('You have enabled auto-renew on membership (id=' . $membership['id'] . ') but the frequencies do not match! The membership will not be auto-renewed.'); | ||
continue; | ||
} | ||
|
||
$membershipParams = array( | ||
'id' => $membership->id, | ||
'contact_id' => $membership->contact_id, | ||
'is_test' => $membership->is_test, | ||
'membership_type_id' => $membership->membership_type_id, | ||
'id' => $membership['id'], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add space before
line There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
'contact_id' => $membership['contact_id'], | ||
'is_test' => $membership['is_test'], | ||
'membership_type_id' => $membership['membership_type_id'], | ||
'membership_activity_status' => 'Completed', | ||
); | ||
|
||
$currentMembership = CRM_Member_BAO_Membership::getContactMembership($membershipParams['contact_id'], | ||
$membershipParams['membership_type_id'], | ||
$membershipParams['is_test'], | ||
$membershipParams['id'] | ||
); | ||
|
||
// CRM-8141 update the membership type with the value recorded in log when membership created/renewed | ||
// this picks up membership type changes during renewals | ||
// @todo this is almost certainly an obsolete sql call, the pre-change | ||
// membership is accessible via $this->_relatedObjects | ||
$sql = " | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmmm, but what if the membership type changed ? , your new code removed this query that handle that but did not offer a replacement There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @omarabuhussein Can you give an example of when this could happen? I thought from the comments that we can just get the pre-change membership directly. |
||
SELECT membership_type_id | ||
FROM civicrm_membership_log | ||
WHERE membership_id={$membershipParams['id']} | ||
ORDER BY id DESC | ||
LIMIT 1;"; | ||
$dao = CRM_Core_DAO::executeQuery($sql); | ||
if ($dao->fetch()) { | ||
if (!empty($dao->membership_type_id)) { | ||
$membershipParams['membership_type_id'] = $dao->membership_type_id; | ||
} | ||
} | ||
$dao->free(); | ||
|
||
$membershipParams['num_terms'] = $contribution->getNumTermsByContributionAndMembershipType( | ||
$membershipParams['membership_type_id'], | ||
$primaryContributionID | ||
); | ||
// @todo remove all this stuff in favour of letting the api call further down handle in | ||
// (it is a duplication of what the api does). | ||
$dates = array_fill_keys(array( | ||
'join_date', | ||
'start_date', | ||
'end_date', | ||
), NULL); | ||
if ($currentMembership) { | ||
|
||
// Does the contact have a "current" membership (ie. not expired/pending etc). | ||
if (!empty($membership['status_id.is_current_member'])) { | ||
/* | ||
* Fixed FOR CRM-4433 | ||
* In BAO/Membership.php(renewMembership function), we skip the extend membership date and status | ||
* when Contribution mode is notify and membership is for renewal ) | ||
* FIXME: Is this still required? | ||
*/ | ||
CRM_Member_BAO_Membership::fixMembershipStatusBeforeRenew($currentMembership, $changeDate); | ||
|
||
// @todo - we should pass membership_type_id instead of null here but not | ||
// adding as not sure of testing | ||
$dates = CRM_Member_BAO_MembershipType::getRenewalDatesForMembershipType($membershipParams['id'], | ||
$changeDate, NULL, $membershipParams['num_terms'] | ||
); | ||
$dates['join_date'] = $currentMembership['join_date']; | ||
CRM_Member_BAO_Membership::fixMembershipStatusBeforeRenew($membership, $changeDate); | ||
} | ||
|
||
//get the status for membership. | ||
$calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($dates['start_date'], | ||
$dates['end_date'], | ||
$dates['join_date'], | ||
'today', | ||
TRUE, | ||
$membershipParams['membership_type_id'], | ||
$membershipParams | ||
); | ||
|
||
unset($dates['end_date']); | ||
$membershipParams['status_id'] = CRM_Utils_Array::value('id', $calcStatus, 'New'); | ||
//we might be renewing membership, | ||
//so make status override false. | ||
// Tell the Membership BAO to calculate membership status. | ||
$membershipParams['skipStatusCal'] = 0; | ||
$membershipParams['exclude_is_admin'] = TRUE; | ||
$membershipParams['is_override'] = FALSE; | ||
$membershipParams['status_override_end_date'] = 'null'; | ||
|
||
//CRM-17723 - reset static $relatedContactIds array() | ||
// @todo move it to Civi Statics. | ||
$var = TRUE; | ||
CRM_Member_BAO_Membership::createRelatedMemberships($var, $var, TRUE); | ||
civicrm_api3('Membership', 'create', $membershipParams); | ||
} | ||
|
||
//CRM-17723 - reset static $relatedContactIds array() | ||
// @todo move it to Civi Statics. | ||
$var = TRUE; | ||
CRM_Member_BAO_Membership::createRelatedMemberships($var, $var, TRUE); | ||
civicrm_api3('Membership', 'create', $membershipParams); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this part a change in behaviour?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eileenmcnaughton I believe the expected behaviour was that you could have multiple memberships linked to a single recurring contribution and all of the code except this one point supports that. I believe it actually fixes a bug. It relates directly to the notes on the PR:
Part of the issue with this area of code is that there is no documentation about what it is actually meant to do! I am still getting reports of auto-renewals failing for some clients in obscure circumstances but I really need this patch reviewing/merging before I can get to the bottom of those issues (as the current core code is littered with inconsistencies including comparisons using getLabel instead of getKey/getName).