Eileen has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/342554 )
Change subject: Restore blanked addresses overwritten on merge. ...................................................................... Restore blanked addresses overwritten on merge. Restore blanked addresses overwritten on merge. I feel like this is a pretty cautious approach to restoring the simplest cases where blank addresses overwrote real addresses. Bug: T159408 Change-Id: Ia10d5095073afb652b552d9af5c36b8f204d1d90 --- M sites/all/modules/wmf_civicrm/tests/phpunit/MergeTest.php A sites/all/modules/wmf_civicrm/update_restore_addresses.php M sites/all/modules/wmf_civicrm/wmf_civicrm.install 3 files changed, 217 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/wikimedia/fundraising/crm refs/changes/54/342554/1 diff --git a/sites/all/modules/wmf_civicrm/tests/phpunit/MergeTest.php b/sites/all/modules/wmf_civicrm/tests/phpunit/MergeTest.php index 5263d4f..4bb13eb 100644 --- a/sites/all/modules/wmf_civicrm/tests/phpunit/MergeTest.php +++ b/sites/all/modules/wmf_civicrm/tests/phpunit/MergeTest.php @@ -1226,4 +1226,66 @@ return $contact['id']; } + /** + * Test recovery where a blank email has overwritten a non-blank email on merge. + * + * In this case an email existed during merge that held no data. It was used + * on the merge, but now we want the lost data. + */ + public function testRepairBlankedEmailOnMerge() { + civicrm_api3('Setting', 'create', array( + 'logging_no_trigger_permission' => 0, + )); + civicrm_api3('Setting', 'create', array('logging' => 1)); + + CRM_Core_DAO::executeQuery('DROP TABLE IF EXISTS blank_addresses'); + require_once __DIR__ . '/../../wmf_civicrm.install'; + require_once __DIR__ . '/../../update_restore_addresses.php'; + wmf_civicrm_update_7475(); + $this->replicateBlankedAddress(); + + $address = $this->callAPISuccessGetSingle('Address', array('contact_id' => $this->contactID)); + $this->assertTrue(empty($address['street_address'])); + + wmf_civicrm_fix_blanked_address($address['id']); + $address = $this->callAPISuccessGetSingle('Address', array('contact_id' => $this->contactID)); + $this->assertEquals('25 Mousey Way', $address['street_address']); + + CRM_Core_DAO::executeQuery('DROP TABLE blank_addresses'); + + civicrm_api3('Setting', 'create', array( + 'logging_no_trigger_permission' => 1, + )); + } + + /** + * Replicate the merge that would result in a blanked address. + */ + protected function replicateBlankedAddress() { + $this->contributionCreate(array( + 'contact_id' => $this->contactID, + 'receive_date' => '2010-01-01', + 'invoice_id' => 1, + 'trxn_id' => 1 + )); + $this->contributionCreate(array( + 'contact_id' => $this->contactID2, + 'receive_date' => '2012-01-01', + 'invoice_id' => 2, + 'trxn_id' => 2 + )); + $this->callAPISuccess('Address', 'create', array( + 'street_address' => '25 Mousey Way', + 'country_id' => 'US', + 'contact_id' => $this->contactID, + 'location_type_id' => 'Main', + )); + $this->callAPISuccess('Address', 'create', array( + 'street_address' => NULL, + 'contact_id' => $this->contactID2, + 'location_type_id' => 'Main', + )); + $this->callAPISuccess('Job', 'process_batch_merge', array('mode' => 'safe')); + } + } diff --git a/sites/all/modules/wmf_civicrm/update_restore_addresses.php b/sites/all/modules/wmf_civicrm/update_restore_addresses.php new file mode 100644 index 0000000..467c7be --- /dev/null +++ b/sites/all/modules/wmf_civicrm/update_restore_addresses.php @@ -0,0 +1,121 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: emcnaughton + * Date: 3/8/17 + * Time: 2:44 PM + */ +/** + * Fix data loss through blanked address. + * + * We have a situation where a number of addresses have been overwritten by blank addresses. + * + * Unfortunately some of these have since been merged, resulting in some unravelling to be done. + * + * This update seeks to unravel ones with a single merge since then. + * + * @param int $addressID + */ +function wmf_civicrm_fix_blanked_address($addressID) { + $result = CRM_Core_DAO::executeQuery('SELECT * FROM log_civicrm_address WHERE id = %1 ORDER BY log_date', array( + 1 => array( + $addressID, + 'Int' + ) + )); + while ($result->fetch()) { + $logEntries[] = (array) $result; + } + if (count($logEntries) === 2 + && (in_array($logEntries[0]['log_action'], array('Insert', 'Initialize')) + && $logEntries[1]['log_action'] === 'Update' + ) + ) { + // we only have a create & an update, not a long complicated history. + $dataFields = array( + 'street_address', + 'city', + 'postal_code', + 'state_province_id', + 'country_id', + 'supplemental_address_1', + 'supplemental_address_2', + 'supplemental_address_3', + 'county_id', + 'postal_code_suffix', + 'name' + ); + foreach ($dataFields as $dataField) { + if (!empty($logEntries[0][$dataField]) || !empty($logEntries[1][$dataField])) { + // Not blank enough to process. + return; + } + } + if ($logEntries[0]['contact_id'] == $logEntries[1]['contact_id']) { + // We are specifically trying to handle merged data at this stage. + // May extend if we identify other patterns. + return; + } + // We definitely have a situation where a blank record was inserted & then, on merge + // was transferred to another contact. We have the log_conn_id & the new contact id. + // let's make sure the new contact has not since been deleted (merged) + // and that they still have no address. + // + // If all that follows we should have a fairly simple case of address rescue. + $keptContact = civicrm_api3('Contact', 'get', array( + 'id' => $logEntries[1]['contact_id'], + 'sequential' => 1 + )); + if ($keptContact['count'] == 0 || !empty($keptContact['values'][0]['is_deleted'])) { + // subject to a subsequent merge & deleted, next round for these. + return; + } + $addresses = civicrm_api3('Address', 'get', array( + 'contact_id' => $logEntries[1]['contact_id'], + 'sequential' => 1 + )); + if ($addresses['count'] != 1 || $addresses['values'][0]['id'] != $addressID) { + // Not just dealing with one address = complexity = later. + return; + } + $logs = civicrm_api3('Logging', 'get', array( + 'tables' => array('civicrm_address'), + 'log_conn_id' => $logEntries[1]['log_conn_id'] + )); + $updates = array(); + foreach ($logs['values'] as $log) { + // We don't want the blank address id, we know that was always blank + if ($log['id'] != $addressID) { + if ($log['action'] !== 'Delete') { + // Unhandled situation if there is a mix of actions. Skip for now. + return; + } + if (isset($retrievalID) && $log['id'] != $retrievalID) { + // another just in case check. Thinking it is only valid for + // 2 addresses to be in the update, our blank address & the + // one it wiped. Any more, bail. + return; + } + $retrievalID = $log['id']; + $fieldName = $log['field']; + if (in_array($fieldName, $dataFields)) { + $updates[$log['field']] = $log['from']; + } + } + } + if (empty($updates)) { + // This is probably overkill, can't see how this would occur. + return; + } + $updates['id'] = $addressID; + civicrm_api3('Address', 'create', $updates); + // Remove from the tracking table. + CRM_Core_DAO::executeQuery('DELETE FROM blank_addresses WHERE id = %1', array( + 1 => array( + $addressID, + 'Int' + ) + )); + + } +} diff --git a/sites/all/modules/wmf_civicrm/wmf_civicrm.install b/sites/all/modules/wmf_civicrm/wmf_civicrm.install index 399e107..e02ccea 100644 --- a/sites/all/modules/wmf_civicrm/wmf_civicrm.install +++ b/sites/all/modules/wmf_civicrm/wmf_civicrm.install @@ -2827,3 +2827,37 @@ wmf_civicrm_update_7485(); wmf_civicrm_update_7490(); } + +/** + * Restore blanked addresses overwritten on merge.. + * + * Bug: T159408 + */ +function wmf_civicrm_update_7510() { + civicrm_initialize(); + require_once 'update_restore_addresses.php'; + $batch = 50000; + $start = 0; + $maxID = CRM_Core_DAO::singleValueQuery('SELECT max(id) FROM civicrm_address + WHERE street_address IS NULL + AND city IS NULL + AND postal_code IS NULL + AND state_province_id IS NULL + AND country_id IS NULL' + ); + while ($start < $maxID) { + $result = CRM_Core_DAO::executeQuery(" + SELECT id FROM civicrm_address + WHERE street_address IS NULL + AND city IS NULL + AND postal_code IS NULL + AND state_province_id IS NULL + AND country_id IS NULL + LIMIT $start, $batch"); + while ($result->fetch()) { + wmf_civicrm_fix_blanked_address($result->id); + } + $start = $start + $batch; + } +} + -- To view, visit https://gerrit.wikimedia.org/r/342554 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ia10d5095073afb652b552d9af5c36b8f204d1d90 Gerrit-PatchSet: 1 Gerrit-Project: wikimedia/fundraising/crm Gerrit-Branch: master Gerrit-Owner: Eileen <emcnaugh...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits