Skip to content

Commit

Permalink
Merge pull request #11298 from otetard/feature/merge_memberships
Browse files Browse the repository at this point in the history
CRM-19151: Add the ability to merge memberships without data loss
  • Loading branch information
eileenmcnaughton authored Mar 22, 2018
2 parents 57ec86b + c31e68a commit 22b09a4
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 1 deletion.
3 changes: 2 additions & 1 deletion CRM/Dedupe/Merger.php
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ public static function cpTables() {
// Empty array == do nothing - this table is handled by mergeGroupContact
'civicrm_subscription_history' => array(),
'civicrm_relationship' => array('CRM_Contact_BAO_Relationship' => 'mergeRelationships'),
'civicrm_membership' => array('CRM_Member_BAO_Membership' => 'mergeMemberships'),
);
}
return $tables;
Expand Down Expand Up @@ -518,7 +519,7 @@ public static function moveContactBelongings($mainId, $otherId, $tables = FALSE,
// Call custom processing function for objects that require it
if (isset($cpTables[$table])) {
foreach ($cpTables[$table] as $className => $fnName) {
$className::$fnName($mainId, $otherId, $sqls);
$className::$fnName($mainId, $otherId, $sqls, $tables, $tableOperations);
}
// Skip normal processing
continue;
Expand Down
127 changes: 127 additions & 0 deletions CRM/Member/BAO/Membership.php
Original file line number Diff line number Diff line change
Expand Up @@ -2570,4 +2570,131 @@ public static function getContactsCancelledMembership($contactID, $isTest = FALS
return $cancelledMembershipIds;
}

/**
* Merges the memberships from otherContactID to mainContactID.
*
* General idea is to merge memberships in regards to their type. We
* move the other contact’s contributions to the main contact’s
* membership which has the same type (if any) and then we update
* membership to avoid loosing `join_date`, `end_date`, and
* `status_id`. In this function, we don’t touch the contributions
* directly (CRM_Dedupe_Merger::moveContactBelongings() takes care
* of it).
*
* This function adds new SQL queries to the $sqlQueries parameter.
*
* @param int $mainContactID
* Contact id of main contact record.
* @param int $otherContactID
* Contact id of record which is going to merge.
* @param array $sqlQueries
* (reference) array of SQL queries to be executed.
* @param array $tables
* List of tables that have to be merged.
* @param array $tableOperations
* Special options/params for some tables to be merged.
*
* @see CRM_Dedupe_Merger::cpTables()
*/
public static function mergeMemberships($mainContactID, $otherContactID, &$sqlQueries, $tables, $tableOperations) {
/*
* If the user requests not to merge memberships but to add them,
* just attribute the `civicrm_membership` to the
* `$mainContactID`. We have to do this here since the general
* merge process is bypassed by this function.
*/
if (array_key_exists("civicrm_membership", $tableOperations) && $tableOperations['civicrm_membership']['add']) {
$sqlQueries[] = "UPDATE IGNORE civicrm_membership SET contact_id = $mainContactID WHERE contact_id = $otherContactID";
return;
}

/*
* Retrieve all memberships that belongs to each contacts and
* keep track of each membership type.
*/
$mainContactMemberships = array();
$otherContactMemberships = array();

$sql = "SELECT id, membership_type_id FROM civicrm_membership membership WHERE contact_id = %1";
$dao = CRM_Core_DAO::executeQuery($sql, array(1 => array($mainContactID, "Integer")));
while ($dao->fetch()) {
$mainContactMemberships[$dao->id] = $dao->membership_type_id;
}

$dao = CRM_Core_DAO::executeQuery($sql, array(1 => array($otherContactID, "Integer")));
while ($dao->fetch()) {
$otherContactMemberships[$dao->id] = $dao->membership_type_id;
}

/*
* For each membership, move related contributions to the main
* contact’s membership (by updating `membership_payments`). Then,
* update membership’s `join_date` (if the other membership’s
* join_date is older) and `end_date` (if the other membership’s
* `end_date` is newer) and `status_id` (if the newly calculated
* status is different).
*
* FIXME: what should we do if we have multiple memberships with
* the same type (currently we only take the first one)?
*/
$newSql = array();
foreach ($otherContactMemberships as $otherMembershipId => $otherMembershipTypeId) {
if ($newMembershipId = array_search($otherMembershipTypeId, $mainContactMemberships)) {

/*
* Move other membership’s contributions to the main one only
* if user requested to merge contributions.
*/
if (!empty($tables) && in_array('civicrm_contribution', $tables)) {
$newSql[] = "UPDATE civicrm_membership_payment SET membership_id=$newMembershipId WHERE membership_id=$otherMembershipId";
}

$sql = "SELECT * FROM civicrm_membership membership WHERE id = %1";

$newMembership = CRM_Member_DAO_Membership::findById($newMembershipId);
$otherMembership = CRM_Member_DAO_Membership::findById($otherMembershipId);

$updates = array();
if (new DateTime($otherMembership->join_date) < new DateTime($newMembership->join_date)) {
$updates["join_date"] = $otherMembership->join_date;
}

if (new DateTime($otherMembership->end_date) > new DateTime($newMembership->end_date)) {
$updates["end_date"] = $otherMembership->end_date;
}

if (count($updates)) {

/*
* Update status
*/
$status = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate(
isset($updates["start_date"]) ? $updates["start_date"] : $newMembership->start_date,
isset($updates["end_date"]) ? $updates["end_date"] : $newMembership->end_date,
isset($updates["join_date"]) ? $updates["join_date"] : $newMembership->join_date,
'today',
FALSE,
$newMembershipId,
$newMembership
);

if (!empty($status['id']) and $status['id'] != $newMembership->status_id) {
$updates['status_id'] = $status['id'];
}

$updates_sql = [];
foreach ($updates as $k => $v) {
$updates_sql[] = "$k = '{$v}'";
}

$newSql[] = sprintf("UPDATE civicrm_membership SET %s WHERE id=%s", implode(", ", $updates_sql), $newMembershipId);
$newSql[] = sprintf("DELETE FROM civicrm_membership WHERE id=%s", $otherMembershipId);
}

}
}

$sqlQueries = array_merge($sqlQueries, $newSql);
}

}

0 comments on commit 22b09a4

Please sign in to comment.