From 6ebecfeaf779000537bc9ef63e1fceb1af3ee195 Mon Sep 17 00:00:00 2001 From: eileen Date: Tue, 27 Jun 2017 14:23:50 +1200 Subject: [PATCH] CRM-20759 enable Primary as an import location type. This goes to the default location type if the contact has no primary, or overwrites primary, in keeping with profile behaviour --- CRM/Contact/BAO/Contact.php | 19 +++ CRM/Contact/Import/Form/MapField.php | 18 +-- CRM/Contact/Import/ImportJob.php | 7 +- CRM/Contact/Import/Parser/Contact.php | 22 ++- CRM/Core/BAO/Mapping.php | 14 +- .../CRM/Contact/Import/Parser/ContactTest.php | 137 +++++++++++++++++- 6 files changed, 197 insertions(+), 20 deletions(-) diff --git a/CRM/Contact/BAO/Contact.php b/CRM/Contact/BAO/Contact.php index 3e95bcd97b..58d030770e 100644 --- a/CRM/Contact/BAO/Contact.php +++ b/CRM/Contact/BAO/Contact.php @@ -3587,4 +3587,23 @@ LEFT JOIN civicrm_address add2 ON ( add1.master_id = add2.id ) return $ids[0]; } + /** + * Check if a field is associated with an entity that has a location type. + * + * (ie. is an address, phone, email etc field). + * + * @param string $fieldTitle + * Title of the field (not the name - create a new function for that if required). + * + * @return bool + */ + public static function isFieldHasLocationType($fieldTitle) { + foreach (CRM_Contact_BAO_Contact::importableFields() as $key => $field) { + if ($field['title'] === $fieldTitle) { + return CRM_Utils_Array::value('hasLocationType', $field); + } + } + return FALSE; + } + } diff --git a/CRM/Contact/Import/Form/MapField.php b/CRM/Contact/Import/Form/MapField.php index 5f265636f5..838efaac6e 100644 --- a/CRM/Contact/Import/Form/MapField.php +++ b/CRM/Contact/Import/Form/MapField.php @@ -209,11 +209,10 @@ class CRM_Contact_Import_Form_MapField extends CRM_Import_Form_MapField { else { $savedMapping = $this->get('savedMapping'); - list($mappingName, $mappingContactType, $mappingLocation, $mappingPhoneType, $mappingImProvider, $mappingRelation, $mappingOperator, $mappingValue, $mappingWebsiteType) = CRM_Core_BAO_Mapping::getMappingFields($savedMapping); + list($mappingName, $mappingContactType, $mappingLocation, $mappingPhoneType, $mappingImProvider, $mappingRelation, $mappingOperator, $mappingValue, $mappingWebsiteType) = CRM_Core_BAO_Mapping::getMappingFields($savedMapping, TRUE); //get loaded Mapping Fields $mappingName = CRM_Utils_Array::value(1, $mappingName); - $mappingContactType = CRM_Utils_Array::value(1, $mappingContactType); $mappingLocation = CRM_Utils_Array::value(1, $mappingLocation); $mappingPhoneType = CRM_Utils_Array::value(1, $mappingPhoneType); $mappingImProvider = CRM_Utils_Array::value(1, $mappingImProvider); @@ -250,8 +249,7 @@ class CRM_Contact_Import_Form_MapField extends CRM_Import_Form_MapField { $dataPatterns = $this->get('dataPatterns'); $hasLocationTypes = $this->get('fieldTypes'); - $this->_location_types = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); - + $this->_location_types = array_merge(array('Primary' => ts('Primary')), CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id')); $defaultLocationType = CRM_Core_BAO_LocationType::getDefault(); // Pass default location to js @@ -724,6 +722,7 @@ class CRM_Contact_Import_Form_MapField extends CRM_Import_Form_MapField { $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id'); $websiteTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Website', 'website_type_id'); $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); + $locationTypes['Primary'] = ts('Primary'); for ($i = 0; $i < $this->_columnCount; $i++) { @@ -735,7 +734,7 @@ class CRM_Contact_Import_Form_MapField extends CRM_Import_Form_MapField { $mapperKeysMain[$i] = $fldName; //need to differentiate non location elements. - if ($selOne && is_numeric($selOne)) { + if ($selOne && (is_numeric($selOne) || $selOne === 'Primary')) { if ($fldName == 'url') { $parserParameters['mapperWebsiteType'][$i] = $websiteTypes[$selOne]; } @@ -805,8 +804,6 @@ class CRM_Contact_Import_Form_MapField extends CRM_Import_Form_MapField { //Updating Mapping Records if (!empty($params['updateMapping'])) { - $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); - $mappingFields = new CRM_Core_DAO_MappingField(); $mappingFields->mapping_id = $params['mappingId']; $mappingFields->find(); @@ -863,8 +860,9 @@ class CRM_Contact_Import_Form_MapField extends CRM_Import_Form_MapField { elseif (CRM_Utils_Array::value('0', $mapperKeys[$i]) == 'im') { $updateMappingFields->im_provider_id = isset($mapperKeys[$i][2]) ? $mapperKeys[$i][2] : NULL; } - $location = array_keys($locationTypes, $locations[$i]); - $updateMappingFields->location_type_id = (isset($location) && isset($location[0])) ? $location[0] : NULL; + $locationTypeID = $parserParameters['mapperLocType'][$i]; + // location_type_id is NULL for non-location fields, and for Primary location. + $updateMappingFields->location_type_id = is_numeric($locationTypeID) ? $locationTypeID : 'null'; } } $updateMappingFields->save(); @@ -921,7 +919,7 @@ class CRM_Contact_Import_Form_MapField extends CRM_Import_Form_MapField { elseif (CRM_Utils_Array::value('1', $mapperKeys[$i]) == 'im') { $saveMappingFields->im_provider_id = isset($mapperKeys[$i][3]) ? $mapperKeys[$i][3] : NULL; } - $saveMappingFields->location_type_id = isset($mapperKeys[$i][2]) ? $mapperKeys[$i][2] : NULL; + $saveMappingFields->location_type_id = (isset($mapperKeys[$i][2]) && $mapperKeys[$i][2] !== 'Primary') ? $mapperKeys[$i][2] : NULL; } } else { diff --git a/CRM/Contact/Import/ImportJob.php b/CRM/Contact/Import/ImportJob.php index 0b37b80c2b..a8bb8590a3 100644 --- a/CRM/Contact/Import/ImportJob.php +++ b/CRM/Contact/Import/ImportJob.php @@ -138,12 +138,12 @@ class CRM_Contact_Import_ImportJob { */ public function runImport(&$form, $timeout = 55) { $mapper = $this->_mapper; - $mapperFields; + $mapperFields = array(); $parserParameters = CRM_Contact_Import_Parser_Contact::getParameterForParser(count($mapper)); $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id'); $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id'); $websiteTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Website', 'website_type_id'); - $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); + $locationTypes = array_merge(array('Primary' => ts('Primary')), CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id')); foreach ($mapper as $key => $value) { @@ -155,7 +155,8 @@ class CRM_Contact_Import_ImportJob { $this->_mapperKeys[$key] = $fldName; //need to differentiate non location elements. - if ($selOne && is_numeric($selOne)) { + // @todo merge this with duplicate code on MapField class. + if ($selOne && (is_numeric($selOne) || $selOne === 'Primary')) { if ($fldName == 'url') { $header[] = $websiteTypes[$selOne]; $parserParameters['mapperWebsiteType'][$key] = $selOne; diff --git a/CRM/Contact/Import/Parser/Contact.php b/CRM/Contact/Import/Parser/Contact.php index 8d171b1a26..9f9733b74a 100644 --- a/CRM/Contact/Import/Parser/Contact.php +++ b/CRM/Contact/Import/Parser/Contact.php @@ -1677,7 +1677,6 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Contact_Import_Parser { //get the prefix id etc if exists CRM_Contact_BAO_Contact::resolveDefaults($formatted, TRUE); - require_once 'CRM/Utils/DeprecatedUtils.php'; //@todo direct call to API function not supported. // setting required check to false, CRM-2839 // plus we do our own required check in import @@ -1955,7 +1954,7 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Contact_Import_Parser { } if (!$break) { - list($value, $formatted) = $this->formatContactParameters(); + $this->formatContactParameters($value, $formatted); } } if (!$isAddressCustomField) { @@ -2445,6 +2444,15 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Contact_Import_Parser { $params[$blockFieldName][++$blockCnt] ); + if ($values['location_type_id'] === 'Primary') { + if (!empty($params['id'])) { + $primary = civicrm_api3($block, 'get', array('return' => 'location_type_id', 'contact_id' => $params['id'], 'is_primary' => 1, 'sequential' => 1)); + } + $defaultLocationType = CRM_Core_BAO_LocationType::getDefault(); + $values['location_type_id'] = (isset($primary) && $primary['count']) ? $primary['values'][0]['location_type_id'] : $defaultLocationType->id; + $values['is_primary'] = 1; + } + if (empty($params['id']) && ($blockCnt == 1)) { $params[$blockFieldName][$blockCnt]['is_primary'] = TRUE; } @@ -2542,6 +2550,16 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Contact_Import_Parser { } } + if ($values['location_type_id'] === 'Primary') { + if (!empty($params['id'])) { + $primary = civicrm_api3('Address', 'get', array('return' => 'location_type_id', 'contact_id' => $params['id'], 'is_primary' => 1, 'sequential' => 1)); + } + $defaultLocationType = CRM_Core_BAO_LocationType::getDefault(); + $params['address'][$addressCnt]['location_type_id'] = (isset($primary) && $primary['count']) ? $primary['values'][0]['location_type_id'] : $defaultLocationType->id; + $params['address'][$addressCnt]['is_primary'] = 1; + + } + if ($addressCnt == 1) { $params['address'][$addressCnt]['is_primary'] = TRUE; diff --git a/CRM/Core/BAO/Mapping.php b/CRM/Core/BAO/Mapping.php index bebf732f30..398ccdb966 100644 --- a/CRM/Core/BAO/Mapping.php +++ b/CRM/Core/BAO/Mapping.php @@ -130,10 +130,14 @@ class CRM_Core_BAO_Mapping extends CRM_Core_DAO_Mapping { * @param int $mappingId * Mapping id. * + * @param bool $addPrimary + * Add the key 'Primary' when the field is a location field AND there is + * no location type (meaning Primary)? + * * @return array * array of mapping fields */ - public static function getMappingFields($mappingId) { + public static function getMappingFields($mappingId, $addPrimary = FALSE) { //mapping is to be loaded from database $mapping = new CRM_Core_DAO_MappingField(); $mapping->mapping_id = $mappingId; @@ -149,6 +153,14 @@ class CRM_Core_BAO_Mapping extends CRM_Core_DAO_Mapping { if (!empty($mapping->location_type_id)) { $mappingLocation[$mapping->grouping][$mapping->column_number] = $mapping->location_type_id; } + elseif ($addPrimary) { + if (CRM_Contact_BAO_Contact::isFieldHasLocationType($mapping->name)) { + $mappingLocation[$mapping->grouping][$mapping->column_number] = ts('Primary'); + } + else { + $mappingLocation[$mapping->grouping][$mapping->column_number] = NULL; + } + } if (!empty($mapping->phone_type_id)) { $mappingPhoneType[$mapping->grouping][$mapping->column_number] = $mapping->phone_type_id; diff --git a/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php b/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php index 712efbaddf..c063623689 100644 --- a/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php +++ b/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php @@ -140,7 +140,7 @@ class CRM_Contact_Imports_Parser_ContactTest extends CiviUnitTestCase { } /** - * Test that the import parser changes the external identifier when there is a dedupe match. + * Test that the import parser adds the address to the right location. * * @throws \Exception */ @@ -149,10 +149,134 @@ class CRM_Contact_Imports_Parser_ContactTest extends CiviUnitTestCase { $contactValues['nick_name'] = 'Old Bill'; $contactValues['external_identifier'] = 'android'; $contactValues['street_address'] = 'Big Mansion'; - $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, array(0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => 2)); + $contactValues['phone'] = '911'; + $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, array(0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => 2, 6 => 2)); $address = $this->callAPISuccessGetSingle('Address', array('street_address' => 'Big Mansion')); $this->assertEquals(2, $address['location_type_id']); + $phone = $this->callAPISuccessGetSingle('Phone', array('phone' => '911')); + $this->assertEquals(2, $phone['location_type_id']); + + $contact = $this->callAPISuccessGetSingle('Contact', $contactValues); + $this->callAPISuccess('Contact', 'delete', array('id' => $contact['id'])); + } + + /** + * Test that the import parser adds the address to the primary location. + * + * @throws \Exception + */ + public function testImportPrimaryAddress() { + list($contactValues) = $this->setUpBaseContact(); + $contactValues['nick_name'] = 'Old Bill'; + $contactValues['external_identifier'] = 'android'; + $contactValues['street_address'] = 'Big Mansion'; + $contactValues['phone'] = 12334; + $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, array(0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => 'Primary', 6 => 'Primary')); + $address = $this->callAPISuccessGetSingle('Address', array('street_address' => 'Big Mansion')); + $this->assertEquals(1, $address['location_type_id']); + + $phone = $this->callAPISuccessGetSingle('Phone', array('phone' => '12334')); + $this->assertEquals(1, $phone['location_type_id']); + + $contact = $this->callAPISuccessGetSingle('Contact', $contactValues); + $this->callAPISuccess('Contact', 'delete', array('id' => $contact['id'])); + } + + /** + * Test that the import parser adds the address to the primary location. + * + * @throws \Exception + */ + public function testImportTwoAddressFirstPrimary() { + list($contactValues) = $this->setUpBaseContact(); + $contactValues['nick_name'] = 'Old Bill'; + $contactValues['external_identifier'] = 'android'; + $contactValues['street_address'] = 'Big Mansion'; + $contactValues['phone'] = 12334; + $fields = array_keys($contactValues); + $contactValues['street_address_2'] = 'Teeny Mansion'; + $contactValues['phone_2'] = 4444; + $fields[] = 'street_address'; + $fields[] = 'phone'; + $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, array(0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => 'Primary', 6 => 'Primary', 7 => 3, 8 => 3), $fields); + $contact = $this->callAPISuccessGetSingle('Contact', array('external_identifier' => 'android')); + $address = $this->callAPISuccess('Address', 'get', array('contact_id' => $contact['id'], 'sequential' => 1)); + + $this->assertEquals(3, $address['values'][0]['location_type_id']); + $this->assertEquals(0, $address['values'][0]['is_primary']); + $this->assertEquals('Teeny Mansion', $address['values'][0]['street_address']); + + $this->assertEquals(1, $address['values'][1]['location_type_id']); + $this->assertEquals(1, $address['values'][1]['is_primary']); + $this->assertEquals('Big Mansion', $address['values'][1]['street_address']); + + $phone = $this->callAPISuccess('Phone', 'get', array('contact_id' => $contact['id'], 'sequential' => 1)); + $this->assertEquals(1, $phone['values'][0]['location_type_id']); + $this->assertEquals(1, $phone['values'][0]['is_primary']); + $this->assertEquals(12334, $phone['values'][0]['phone']); + $this->assertEquals(3, $phone['values'][1]['location_type_id']); + $this->assertEquals(0, $phone['values'][1]['is_primary']); + $this->assertEquals(4444, $phone['values'][1]['phone']); + + $this->callAPISuccess('Contact', 'delete', array('id' => $contact['id'])); + } + + /** + * Test that the import parser adds the address to the primary location. + * + * @throws \Exception + */ + public function testImportTwoAddressSecondPrimary() { + list($contactValues) = $this->setUpBaseContact(); + $contactValues['nick_name'] = 'Old Bill'; + $contactValues['external_identifier'] = 'android'; + $contactValues['street_address'] = 'Big Mansion'; + $contactValues['phone'] = 12334; + $fields = array_keys($contactValues); + $contactValues['street_address_2'] = 'Teeny Mansion'; + $contactValues['phone_2'] = 4444; + $fields[] = 'street_address'; + $fields[] = 'phone'; + $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, array(0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => 3, 6 => 3, 7 => 'Primary', 8 => 'Primary'), $fields); + $contact = $this->callAPISuccessGetSingle('Contact', array('external_identifier' => 'android')); + $address = $this->callAPISuccess('Address', 'get', array('contact_id' => $contact['id'], 'sequential' => 1)); + + $this->assertEquals(1, $address['values'][0]['location_type_id']); + $this->assertEquals(1, $address['values'][0]['is_primary']); + $this->assertEquals('Teeny Mansion', $address['values'][0]['street_address']); + + $this->assertEquals(3, $address['values'][1]['location_type_id']); + $this->assertEquals(0, $address['values'][1]['is_primary']); + $this->assertEquals('Big Mansion', $address['values'][1]['street_address']); + + $phone = $this->callAPISuccess('Phone', 'get', array('contact_id' => $contact['id'], 'sequential' => 1)); + $this->assertEquals(3, $phone['values'][0]['location_type_id']); + $this->assertEquals(0, $phone['values'][0]['is_primary']); + $this->assertEquals(12334, $phone['values'][0]['phone']); + $this->assertEquals(1, $phone['values'][1]['location_type_id']); + $this->assertEquals(1, $phone['values'][1]['is_primary']); + $this->assertEquals(4444, $phone['values'][1]['phone']); + + $this->callAPISuccess('Contact', 'delete', array('id' => $contact['id'])); + } + + /** + * Test that the import parser updates the address on the existing primary location. + * + * @throws \Exception + */ + public function testImportPrimaryAddressUpdate() { + list($contactValues) = $this->setUpBaseContact(array('external_identifier' => 'android')); + $contactValues['nick_name'] = 'Old Bill'; + $contactValues['external_identifier'] = 'android'; + $contactValues['street_address'] = 'Big Mansion'; + $contactID = $this->callAPISuccessGetValue('Contact', array('external_identifier' => 'android', 'return' => 'id')); + $originalAddress = $this->callAPISuccess('Address', 'create', array('location_type_id' => 2, 'street_address' => 'small house', 'contact_id' => $contactID)); + $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, array(0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => 'Primary')); + $address = $this->callAPISuccessGetSingle('Address', array('street_address' => 'Big Mansion')); + $this->assertEquals(2, $address['location_type_id']); + $this->assertEquals($originalAddress['id'], $address['id']); $this->callAPISuccessGetSingle('Contact', $contactValues); } @@ -164,9 +288,14 @@ class CRM_Contact_Imports_Parser_ContactTest extends CiviUnitTestCase { * @param int $onDuplicateAction * @param int $expectedResult * @param array|null $mapperLocType + * @param array|null $fields + * Array of field names. Will be calculated from $originalValues if not passed in, but + * that method does not cope with duplicates. */ - protected function runImport($originalValues, $onDuplicateAction, $expectedResult, $mapperLocType = NULL) { - $fields = array_keys($originalValues); + protected function runImport($originalValues, $onDuplicateAction, $expectedResult, $mapperLocType = NULL, $fields = NULL) { + if (!$fields) { + $fields = array_keys($originalValues); + } $values = array_values($originalValues); $parser = new CRM_Contact_Import_Parser_Contact($fields, $mapperLocType); $parser->_contactType = 'Individual'; -- 2.25.1