From c11e71bd973aca86770e5f9657390ddb55d70812 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Wed, 18 May 2022 11:09:33 +1200 Subject: [PATCH] Validate subtype change --- CRM/Contact/Import/Parser/Contact.php | 24 ++++----- .../CRM/Contact/Import/Parser/ContactTest.php | 51 ++++++++++++++++++- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/CRM/Contact/Import/Parser/Contact.php b/CRM/Contact/Import/Parser/Contact.php index 4dcc2b053856..799299efa0fc 100644 --- a/CRM/Contact/Import/Parser/Contact.php +++ b/CRM/Contact/Import/Parser/Contact.php @@ -300,28 +300,26 @@ public function import($onDuplicate, &$values) { $params = $this->getMappedRow($values); $formatted = [ - 'contact_type' => $this->_contactType, + 'contact_type' => $this->getContactType(), ]; $contactFields = CRM_Contact_DAO_Contact::import(); - if (!empty($this->_contactSubType)) { - $params['contact_sub_type'] = $this->_contactSubType; - } + $params['contact_sub_type'] = $this->getContactSubType() ?: ($params['contact_sub_type'] ?? NULL); - if ($subType = CRM_Utils_Array::value('contact_sub_type', $params)) { - if (CRM_Contact_BAO_ContactType::isExtendsContactType($subType, $this->_contactType, FALSE, 'label')) { - $subTypes = CRM_Contact_BAO_ContactType::subTypePairs($this->_contactType, FALSE, NULL); - $params['contact_sub_type'] = array_search($subType, $subTypes); - } - elseif (!CRM_Contact_BAO_ContactType::isExtendsContactType($subType, $this->_contactType)) { - $message = "Mismatched or Invalid Contact Subtype."; - array_unshift($values, $message); - return CRM_Import_Parser::NO_MATCH; + if ($params['contact_sub_type']) { + if (CRM_Contact_BAO_ContactType::isExtendsContactType($params['contact_sub_type'], $this->getContactType(), FALSE, 'label')) { + // I think this bit is switching a passed in label to + // a name. + $subTypes = CRM_Contact_BAO_ContactType::subTypePairs($this->getContactType(), FALSE, NULL); + $params['contact_sub_type'] = array_search($params['contact_sub_type'], $subTypes); } } try { + if ($params['contact_sub_type'] && !CRM_Contact_BAO_ContactType::isExtendsContactType($params['contact_sub_type'], $this->getContactType())) { + throw new CRM_Core_Exception('Mismatched or Invalid Contact Subtype.' . $params['id'], CRM_Import_Parser::NO_MATCH); + } $params['id'] = $formatted['id'] = $this->lookupContactID($params, ($this->isSkipDuplicates() || $this->isIgnoreDuplicates())); } catch (CRM_Core_Exception $e) { diff --git a/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php b/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php index c0f6453e40de..7e812289c84b 100644 --- a/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php +++ b/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php @@ -14,6 +14,7 @@ * File for the CRM_Contact_Imports_Parser_ContactTest class. */ +use Civi\Api4\RelationshipType; use Civi\Api4\UserJob; /** @@ -36,7 +37,8 @@ class CRM_Contact_Import_Parser_ContactTest extends CiviUnitTestCase { * Tear down after test. */ public function tearDown(): void { - $this->quickCleanup(['civicrm_address', 'civicrm_phone', 'civicrm_email', 'civicrm_user_job'], TRUE); + $this->quickCleanup(['civicrm_address', 'civicrm_phone', 'civicrm_email', 'civicrm_user_job', 'civicrm_relationship'], TRUE); + RelationshipType::delete()->addWhere('name_a_b', '=', 'Dad to')->execute(); parent::tearDown(); } @@ -261,9 +263,11 @@ public function testImportParserWithUpdateWithExternalIdentifier(): void { /** * Test updating an existing contact with external_identifier match but subtype mismatch. * + * The subtype is updated, as there is no conflicting contact data. + * * @throws \Exception */ - public function testImportParserWithUpdateWithExternalIdentifierSubtypeMismatch(): void { + public function testImportParserWithUpdateWithExternalIdentifierSubtypeChange(): void { $contactID = $this->individualCreate(['external_identifier' => 'billy', 'first_name' => 'William', 'contact_sub_type' => 'Parent']); $this->runImport([ 'external_identifier' => 'billy', @@ -277,6 +281,27 @@ public function testImportParserWithUpdateWithExternalIdentifierSubtypeMismatch( $this->assertEquals(['Staff'], $contact['contact_sub_type']); } + /** + * Test updating an existing contact with external_identifier match but subtype mismatch. + * + * The subtype is not updated, as there is conflicting contact data. + * + * @throws \Exception + */ + public function testImportParserUpdateWithExternalIdentifierSubtypeChangeFail(): void { + $contactID = $this->individualCreate(['external_identifier' => 'billy', 'first_name' => 'William', 'contact_sub_type' => 'Parent']); + $this->addChild($contactID); + + $this->runImport([ + 'external_identifier' => 'billy', + 'nick_name' => 'Old Bill', + 'contact_sub_type' => 'Staff', + ], CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID); + $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contactID]); + $this->assertEquals('', $contact['nick_name']); + $this->assertEquals(['Parent'], $contact['contact_sub_type']); + } + /** * Test updating an existing contact with external_identifier match but subtype mismatch. * @@ -1224,6 +1249,28 @@ protected function runImport(array $originalValues, $onDuplicateAction, $expecte $this->assertEquals($expectedResult, $parser->import($onDuplicateAction, $values), 'Return code from parser import was not as expected'); } + /** + * @param int $contactID + * + * @throws \API_Exception + * @throws \Civi\API\Exception\UnauthorizedException + */ + protected function addChild(int $contactID): void { + $relatedContactID = $this->individualCreate(); + $relationshipTypeID = RelationshipType::create()->setValues([ + 'name_a_b' => 'Dad to', + 'name_b_a' => 'Sleep destroyer of', + 'contact_type_a' => 'Individual', + 'contact_type_b' => 'Individual', + 'contact_sub_type_a' => 'Parent', + ])->execute()->first()['id']; + \Civi\Api4\Relationship::create()->setValues([ + 'relationship_type_id' => $relationshipTypeID, + 'contact_id_a' => $contactID, + 'contact_id_b' => $relatedContactID, + ])->execute(); + } + /** * @param array $fields Array of fields to be imported * @param array $allfields Array of all fields which can be part of import