3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This code is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
14 * File for the CRM_Contact_Imports_Parser_ContactTest class.
18 * Test contact import parser.
23 class CRM_Contact_Import_Parser_ContactTest
extends CiviUnitTestCase
{
24 use CRMTraits_Custom_CustomDataTrait
;
27 * Main entity for the class.
31 protected $entity = 'Contact';
36 public function setUp() {
41 * Tear down after test.
43 * @throws \CRM_Core_Exception
45 public function tearDown() {
46 $this->quickCleanup(['civicrm_address', 'civicrm_phone', 'civicrm_email'], TRUE);
51 * Test that import parser will add contact with employee of relationship.
55 public function testImportParserWtihEmployeeOfRelationship() {
56 $this->organizationCreate([
57 "organization_name" => "Agileware",
58 "legal_name" => "Agileware",
60 $contactImportValues = [
61 "first_name" => "Alok",
62 "last_name" => "Patel",
63 "Employee of" => "Agileware",
66 $fields = array_keys($contactImportValues);
67 $values = array_values($contactImportValues);
68 $parser = new CRM_Contact_Import_Parser_Contact($fields, []);
69 $parser->_contactType
= 'Individual';
71 $this->mapRelationshipFields($fields, $parser->getAllFields());
73 $parser = new CRM_Contact_Import_Parser_Contact($fields, [], [], [], [
85 ], [], [], [], [], []);
87 $parser->_contactType
= 'Individual';
88 $parser->_onDuplicate
= CRM_Import_Parser
::DUPLICATE_UPDATE
;
91 $this->assertEquals(CRM_Import_Parser
::VALID
, $parser->import(CRM_Import_Parser
::DUPLICATE_UPDATE
, $values), 'Return code from parser import was not as expected');
92 $this->callAPISuccess("Contact", "get", [
93 "first_name" => "Alok",
94 "last_name" => "Patel",
95 "organization_name" => "Agileware",
100 * Test that import parser will not fail when same external_identifier found of deleted contact.
102 * @throws \CRM_Core_Exception
104 public function testImportParserWtihDeletedContactExternalIdentifier() {
105 $contactId = $this->individualCreate([
106 'external_identifier' => 'ext-1',
108 $this->callAPISuccess('Contact', 'delete', ['id' => $contactId]);
109 list($originalValues, $result) = $this->setUpBaseContact([
110 'external_identifier' => 'ext-1',
112 $originalValues['nick_name'] = 'Old Bill';
113 $this->runImport($originalValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
);
114 $originalValues['id'] = $result['id'];
115 $this->assertEquals('ext-1', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'external_identifier']));
116 $this->callAPISuccessGetSingle('Contact', $originalValues);
120 * Test import parser will update based on a rule match.
122 * In this case the contact has no external identifier.
126 public function testImportParserWithUpdateWithoutExternalIdentifier() {
127 list($originalValues, $result) = $this->setUpBaseContact();
128 $originalValues['nick_name'] = 'Old Bill';
129 $this->runImport($originalValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
);
130 $originalValues['id'] = $result['id'];
131 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'nick_name']));
132 $this->callAPISuccessGetSingle('Contact', $originalValues);
136 * Test import parser will update contacts with an external identifier.
138 * This is the basic test where the identifier matches the import parameters.
142 public function testImportParserWithUpdateWithExternalIdentifier() {
143 list($originalValues, $result) = $this->setUpBaseContact(['external_identifier' => 'windows']);
145 $this->assertEquals($result['id'], CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact', 'windows', 'id', 'external_identifier', TRUE));
146 $this->assertEquals('windows', $result['external_identifier']);
148 $originalValues['nick_name'] = 'Old Bill';
149 $this->runImport($originalValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
);
150 $originalValues['id'] = $result['id'];
152 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'nick_name']));
153 $this->callAPISuccessGetSingle('Contact', $originalValues);
157 * Test import parser will fallback to external identifier.
159 * In this case no primary match exists (e.g the details are not supplied) so it falls back on external identifier.
161 * @see https://issues.civicrm.org/jira/browse/CRM-17275
165 public function testImportParserWithUpdateWithExternalIdentifierButNoPrimaryMatch() {
166 list($originalValues, $result) = $this->setUpBaseContact([
167 'external_identifier' => 'windows',
171 $this->assertEquals('windows', $result['external_identifier']);
173 $originalValues['nick_name'] = 'Old Bill';
174 $this->runImport($originalValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
);
175 $originalValues['id'] = $result['id'];
177 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'nick_name']));
178 $this->callAPISuccessGetSingle('Contact', $originalValues);
182 * Test import parser will fallback to external identifier.
184 * In this case no primary match exists (e.g the details are not supplied) so it falls back on external identifier.
186 * @see https://issues.civicrm.org/jira/browse/CRM-17275
190 public function testImportParserWithUpdateWithContactID() {
191 list($originalValues, $result) = $this->setUpBaseContact([
192 'external_identifier' => '',
195 $updateValues = ['id' => $result['id'], 'email' => 'bill@example.com'];
196 // This is some deep weirdness - this sets a flag for updatingBlankLocinfo - allowing input to be blanked
197 // (which IS a good thing but it's pretty weird & all to do with legacy profile stuff).
198 CRM_Core_Session
::singleton()->set('authSrc', CRM_Core_Permission
::AUTH_SRC_CHECKSUM
);
199 $this->runImport($updateValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
, [NULL, 1]);
200 $originalValues['id'] = $result['id'];
201 $this->callAPISuccessGetSingle('Email', ['contact_id' => $originalValues['id'], 'is_primary' => 1]);
202 $this->callAPISuccessGetSingle('Contact', $originalValues);
206 * Test that the import parser adds the external identifier where none is set.
210 public function testImportParserWithUpdateWithNoExternalIdentifier() {
211 list($originalValues, $result) = $this->setUpBaseContact();
212 $originalValues['nick_name'] = 'Old Bill';
213 $originalValues['external_identifier'] = 'windows';
214 $this->runImport($originalValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
);
215 $originalValues['id'] = $result['id'];
216 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'nick_name']));
217 $this->callAPISuccessGetSingle('Contact', $originalValues);
221 * Test that the import parser changes the external identifier when there is a dedupe match.
225 public function testImportParserWithUpdateWithChangedExternalIdentifier() {
226 list($contactValues, $result) = $this->setUpBaseContact(['external_identifier' => 'windows']);
227 $contact_id = $result['id'];
228 $contactValues['nick_name'] = 'Old Bill';
229 $contactValues['external_identifier'] = 'android';
230 $this->runImport($contactValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
);
231 $contactValues['id'] = $contact_id;
232 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $contact_id, 'return' => 'nick_name']));
233 $this->callAPISuccessGetSingle('Contact', $contactValues);
237 * Test that the import parser adds the address to the right location.
241 public function testImportBillingAddress() {
242 list($contactValues) = $this->setUpBaseContact();
243 $contactValues['nick_name'] = 'Old Bill';
244 $contactValues['external_identifier'] = 'android';
245 $contactValues['street_address'] = 'Big Mansion';
246 $contactValues['phone'] = '911';
247 $this->runImport($contactValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
, [0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => 2, 6 => 2]);
248 $address = $this->callAPISuccessGetSingle('Address', ['street_address' => 'Big Mansion']);
249 $this->assertEquals(2, $address['location_type_id']);
251 $phone = $this->callAPISuccessGetSingle('Phone', ['phone' => '911']);
252 $this->assertEquals(2, $phone['location_type_id']);
254 $contact = $this->callAPISuccessGetSingle('Contact', $contactValues);
255 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
259 * Test that the not-really-encouraged way of creating locations via contact.create doesn't mess up primaries.
261 public function testContactLocationBlockHandling() {
262 $id = $this->individualCreate([
265 'location_type_id' => 1,
266 'phone' => '987654321',
269 'location_type_id' => 2,
270 'phone' => '456-7890',
275 'location_type_id' => 1,
279 'location_type_id' => 2,
285 'location_type_id' => 1,
289 'location_type_id' => 2,
295 'location_type_id' => 1,
296 'email' => 'bob@example.com',
299 'location_type_id' => 2,
300 'email' => 'fred@example.com',
304 $phones = $this->callAPISuccess('Phone', 'get', ['contact_id' => $id])['values'];
305 $emails = $this->callAPISuccess('Email', 'get', ['contact_id' => $id])['values'];
306 $openIDs = $this->callAPISuccess('OpenID', 'get', ['contact_id' => $id])['values'];
307 $ims = $this->callAPISuccess('IM', 'get', ['contact_id' => $id])['values'];
308 $this->assertCount(2, $phones);
309 $this->assertCount(2, $emails);
310 $this->assertCount(2, $ims);
311 $this->assertCount(2, $openIDs);
313 $this->assertLocationValidity();
314 $this->callAPISuccess('Contact', 'create', [
316 // This is secret code for 'delete this phone'.
317 'updateBlankLocInfo' => TRUE,
320 'id' => key($phones),
325 'id' => key($emails),
335 'id' => key($openIDs),
339 $this->assertLocationValidity();
340 $this->callAPISuccessGetCount('Phone', ['contact_id' => $id], 1);
341 $this->callAPISuccessGetCount('Email', ['contact_id' => $id], 1);
342 $this->callAPISuccessGetCount('OpenID', ['contact_id' => $id], 1);
343 $this->callAPISuccessGetCount('IM', ['contact_id' => $id], 1);
347 * Test that the import parser adds the address to the primary location.
351 public function testImportPrimaryAddress() {
352 list($contactValues) = $this->setUpBaseContact();
353 $contactValues['nick_name'] = 'Old Bill';
354 $contactValues['external_identifier'] = 'android';
355 $contactValues['street_address'] = 'Big Mansion';
356 $contactValues['phone'] = 12334;
357 $this->runImport($contactValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
, [0 => NULL, 1 => NULL, 2 => 'Primary', 3 => NULL, 4 => NULL, 5 => 'Primary', 6 => 'Primary']);
358 $address = $this->callAPISuccessGetSingle('Address', ['street_address' => 'Big Mansion']);
359 $this->assertEquals(1, $address['location_type_id']);
360 $this->assertEquals(1, $address['is_primary']);
362 $phone = $this->callAPISuccessGetSingle('Phone', ['phone' => '12334']);
363 $this->assertEquals(1, $phone['location_type_id']);
365 $this->callAPISuccessGetSingle('Email', ['email' => 'bill.gates@microsoft.com']);
367 $contact = $this->callAPISuccessGetSingle('Contact', $contactValues);
368 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
372 * Test that address location type id is ignored for dedupe purposes on import.
376 public function testIgnoreLocationTypeId() {
377 // Create a rule that matches on last name and street address.
378 $rgid = $this->createRuleGroup()['id'];
379 $this->callAPISuccess('Rule', 'create', [
380 'dedupe_rule_group_id' => $rgid,
381 'rule_field' => 'last_name',
382 'rule_table' => 'civicrm_contact',
385 $this->callAPISuccess('Rule', 'create', [
386 'dedupe_rule_group_id' => $rgid,
387 'rule_field' => 'street_address',
388 'rule_table' => 'civicrm_address',
391 // Create a contact with an address of location_type_id 1.
393 'contact_type' => 'Individual',
394 'first_name' => 'Original',
395 'last_name' => 'Smith',
397 $contact1 = $this->callAPISuccess('Contact', 'create', $contact1Params);
398 $this->callAPISuccess('Address', 'create', [
399 'contact_id' => $contact1['id'],
400 'location_type_id' => 1,
401 'street_address' => 'Big Mansion',
405 'first_name' => 'New',
406 'last_name' => 'Smith',
407 'street_address' => 'Big Mansion',
410 // We want to import with a location_type_id of 4.
411 $importLocationTypeId = '4';
412 $this->runImport($contactValues, CRM_Import_Parser
::DUPLICATE_SKIP
, CRM_Import_Parser
::DUPLICATE
, [0 => NULL, 1 => NULL, 2 => $importLocationTypeId], NULL, $rgid);
413 $address = $this->callAPISuccessGetSingle('Address', ['street_address' => 'Big Mansion']);
414 $this->assertEquals(1, $address['location_type_id']);
415 $contact = $this->callAPISuccessGetSingle('Contact', $contact1Params);
416 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
420 * Test that address custom fields can be imported
423 * @throws \CRM_Core_Exception
425 public function testAddressWithCustomData() {
426 $ids = $this->entityCustomGroupWithSingleFieldCreate('Address', 'AddressTest.php');
427 list($contactValues) = $this->setUpBaseContact();
428 $contactValues['nick_name'] = 'Old Bill';
429 $contactValues['external_identifier'] = 'android';
430 $contactValues['street_address'] = 'Big Mansion';
431 $contactValues['custom_' . $ids['custom_field_id']] = 'Update';
432 $this->runImport($contactValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
, [0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => 'Primary', 6 => 'Primary']);
433 $address = $this->callAPISuccessGetSingle('Address', ['street_address' => 'Big Mansion', 'return' => 'custom_' . $ids['custom_field_id']]);
434 $this->assertEquals('Update', $address['custom_' . $ids['custom_field_id']]);
438 * Test gender works when you specify the label.
440 * There is an expectation that you can import by label here.
442 * @throws \CRM_Core_Exception
444 public function testGenderLabel() {
446 'first_name' => 'Bill',
447 'last_name' => 'Gates',
448 'email' => 'bill.gates@microsoft.com',
449 'nick_name' => 'Billy-boy',
450 'gender_id' => 'Female',
452 $this->runImport($contactValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
, [NULL, NULL, 'Primary', NULL, NULL]);
453 $this->callAPISuccessGetSingle('Contact', $contactValues);
457 * Test prefix & suffix work when you specify the label.
459 * There is an expectation that you can import by label here.
461 * @throws \CRM_Core_Exception
462 * @throws \CiviCRM_API3_Exception
464 public function testPrefixLabel() {
465 $this->callAPISuccess('OptionValue', 'create', ['option_group_id' => 'individual_prefix', 'name' => 'new_one', 'label' => 'special', 'value' => 70]);
467 ['name' => 'first_name', 'column_number' => 0],
468 ['name' => 'last_name', 'column_number' => 1],
469 ['name' => 'email', 'column_number' => 2, 'location_type_id' => CRM_Core_PseudoConstant
::getKey('CRM_Core_BAO_Email', 'location_type_id', 'Home')],
470 ['name' => 'prefix_id', 'column_number' => 3],
471 ['name' => 'suffix_id', 'column_number' => 4],
473 $processor = new CRM_Import_ImportProcessor();
474 $processor->setMappingFields($mapping);
475 $processor->setContactType('Individual');
476 $importer = $processor->getImporterObject();
481 'bill.gates@microsoft.com',
485 $importer->import(CRM_Import_Parser
::DUPLICATE_NOCHECK
, $contactValues);
487 $contact = $this->callAPISuccessGetSingle('Contact', ['first_name' => 'Bill', 'prefix_id' => 'new_one', 'suffix_id' => 'III']);
488 $this->assertEquals('special Bill Gates III', $contact['display_name']);
492 * Test that labels work for importing custom data.
494 * @throws \CRM_Core_Exception
496 public function testCustomDataLabel() {
497 $this->createCustomGroupWithFieldOfType([], 'select');
499 'first_name' => 'Bill',
500 'last_name' => 'Gates',
501 'email' => 'bill.gates@microsoft.com',
502 'nick_name' => 'Billy-boy',
503 $this->getCustomFieldName('select') => 'Yellow',
505 $this->runImport($contactValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
, [NULL, NULL, 'Primary', NULL, NULL]);
506 $contact = $this->callAPISuccessGetSingle('Contact', array_merge($contactValues, ['return' => $this->getCustomFieldName('select')]));
507 $this->assertEquals('Y', $contact[$this->getCustomFieldName('select')]);
511 * Test that names work for importing custom data.
513 * @throws \CRM_Core_Exception
515 public function testCustomDataName() {
516 $this->createCustomGroupWithFieldOfType([], 'select');
518 'first_name' => 'Bill',
519 'last_name' => 'Gates',
520 'email' => 'bill.gates@microsoft.com',
521 'nick_name' => 'Billy-boy',
522 $this->getCustomFieldName('select') => 'Y',
524 $this->runImport($contactValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
, [NULL, NULL, 'Primary', NULL, NULL]);
525 $contact = $this->callAPISuccessGetSingle('Contact', array_merge($contactValues, ['return' => $this->getCustomFieldName('select')]));
526 $this->assertEquals('Y', $contact[$this->getCustomFieldName('select')]);
530 * Test that the import parser adds the address to the primary location.
534 public function testImportDeceased() {
535 list($contactValues) = $this->setUpBaseContact();
536 CRM_Core_Session
::singleton()->set("dateTypes", 1);
537 $contactValues['birth_date'] = '1910-12-17';
538 $contactValues['deceased_date'] = '2010-12-17';
539 $this->runImport($contactValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
);
540 $contact = $this->callAPISuccessGetSingle('Contact', $contactValues);
541 $this->assertEquals('1910-12-17', $contact['birth_date']);
542 $this->assertEquals('2010-12-17', $contact['deceased_date']);
543 $this->assertEquals(1, $contact['is_deceased']);
544 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
548 * Test that the import parser adds the address to the primary location.
552 public function testImportTwoAddressFirstPrimary() {
553 list($contactValues) = $this->setUpBaseContact();
554 $contactValues['nick_name'] = 'Old Bill';
555 $contactValues['external_identifier'] = 'android';
556 $contactValues['street_address'] = 'Big Mansion';
557 $contactValues['phone'] = 12334;
558 $fields = array_keys($contactValues);
559 $contactValues['street_address_2'] = 'Teeny Mansion';
560 $contactValues['phone_2'] = 4444;
561 $fields[] = 'street_address';
563 $this->runImport($contactValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
, [0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => 'Primary', 6 => 'Primary', 7 => 3, 8 => 3], $fields);
564 $contact = $this->callAPISuccessGetSingle('Contact', ['external_identifier' => 'android']);
565 $address = $this->callAPISuccess('Address', 'get', ['contact_id' => $contact['id'], 'sequential' => 1]);
567 $this->assertEquals(3, $address['values'][0]['location_type_id']);
568 $this->assertEquals(0, $address['values'][0]['is_primary']);
569 $this->assertEquals('Teeny Mansion', $address['values'][0]['street_address']);
571 $this->assertEquals(1, $address['values'][1]['location_type_id']);
572 $this->assertEquals(1, $address['values'][1]['is_primary']);
573 $this->assertEquals('Big Mansion', $address['values'][1]['street_address']);
575 $phone = $this->callAPISuccess('Phone', 'get', ['contact_id' => $contact['id'], 'sequential' => 1]);
576 $this->assertEquals(1, $phone['values'][0]['location_type_id']);
577 $this->assertEquals(1, $phone['values'][0]['is_primary']);
578 $this->assertEquals(12334, $phone['values'][0]['phone']);
579 $this->assertEquals(3, $phone['values'][1]['location_type_id']);
580 $this->assertEquals(0, $phone['values'][1]['is_primary']);
581 $this->assertEquals(4444, $phone['values'][1]['phone']);
583 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
587 * Test importing 2 phones of different types.
589 * @throws \CRM_Core_Exception
590 * @throws \CiviCRM_API3_Exception
592 public function testImportTwoPhonesDifferentTypes() {
593 $processor = new CRM_Import_ImportProcessor();
594 $processor->setContactType('Individual');
595 $processor->setMappingFields(
597 ['name' => 'first_name'],
598 ['name' => 'last_name'],
600 ['name' => 'phone', 'location_type_id' => 1, 'phone_type_id' => 2],
601 ['name' => 'phone', 'location_type_id' => 1, 'phone_type_id' => 1],
604 $importer = $processor->getImporterObject();
605 $fields = ['First Name', 'new last name', 'bob@example.com', '1234', '5678'];
606 $importer->import(CRM_Import_Parser
::DUPLICATE_UPDATE
, $fields);
607 $contact = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'new last name']);
608 $phones = $this->callAPISuccess('Phone', 'get', ['contact_id' => $contact['id']])['values'];
609 $this->assertCount(2, $phones);
613 * Test that the import parser adds the address to the primary location.
617 public function testImportTwoAddressSecondPrimary() {
618 list($contactValues) = $this->setUpBaseContact();
619 $contactValues['nick_name'] = 'Old Bill';
620 $contactValues['external_identifier'] = 'android';
621 $contactValues['street_address'] = 'Big Mansion';
622 $contactValues['phone'] = 12334;
623 $fields = array_keys($contactValues);
624 $contactValues['street_address_2'] = 'Teeny Mansion';
625 $contactValues['phone_2'] = 4444;
626 $fields[] = 'street_address';
628 $this->runImport($contactValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
, [0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => 3, 6 => 3, 7 => 'Primary', 8 => 'Primary'], $fields);
629 $contact = $this->callAPISuccessGetSingle('Contact', ['external_identifier' => 'android']);
630 $address = $this->callAPISuccess('Address', 'get', ['contact_id' => $contact['id'], 'sequential' => 1])['values'];
632 $this->assertEquals(1, $address[1]['location_type_id']);
633 $this->assertEquals(1, $address[1]['is_primary']);
634 $this->assertEquals('Teeny Mansion', $address[1]['street_address']);
636 $this->assertEquals(3, $address[0]['location_type_id']);
637 $this->assertEquals(0, $address[0]['is_primary']);
638 $this->assertEquals('Big Mansion', $address[0]['street_address']);
640 $phone = $this->callAPISuccess('Phone', 'get', ['contact_id' => $contact['id'], 'sequential' => 1, 'options' => ['sort' => 'is_primary DESC']])['values'];
641 $this->assertEquals(3, $phone[1]['location_type_id']);
642 $this->assertEquals(0, $phone[1]['is_primary']);
643 $this->assertEquals(12334, $phone[1]['phone']);
644 $this->assertEquals(1, $phone[0]['location_type_id']);
645 $this->assertEquals(1, $phone[0]['is_primary']);
646 $this->assertEquals(4444, $phone[0]['phone']);
648 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
652 * Test that the import parser updates the address on the existing primary location.
656 public function testImportPrimaryAddressUpdate() {
657 list($contactValues) = $this->setUpBaseContact(['external_identifier' => 'android']);
658 $contactValues['email'] = 'melinda.gates@microsoft.com';
659 $contactValues['phone'] = '98765';
660 $contactValues['external_identifier'] = 'android';
661 $contactValues['street_address'] = 'Big Mansion';
662 $contactValues['city'] = 'Big City';
663 $contactID = $this->callAPISuccessGetValue('Contact', ['external_identifier' => 'android', 'return' => 'id']);
664 $originalAddress = $this->callAPISuccess('Address', 'create', ['location_type_id' => 2, 'street_address' => 'small house', 'contact_id' => $contactID]);
665 $originalPhone = $this->callAPISuccess('phone', 'create', ['location_type_id' => 2, 'phone' => '1234', 'contact_id' => $contactID]);
666 $this->runImport($contactValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
, [0 => NULL, 1 => NULL, 2 => 'Primary', 3 => NULL, 4 => NULL, 5 => 'Primary', 6 => 'Primary', 7 => 'Primary']);
667 $phone = $this->callAPISuccessGetSingle('Phone', ['phone' => '98765']);
668 $this->assertEquals(2, $phone['location_type_id']);
669 $this->assertEquals($originalPhone['id'], $phone['id']);
670 $email = $this->callAPISuccess('Email', 'getsingle', ['contact_id' => $contactID]);
671 $address = $this->callAPISuccessGetSingle('Address', ['street_address' => 'Big Mansion']);
672 $this->assertEquals(2, $address['location_type_id']);
673 $this->assertEquals($originalAddress['id'], $address['id']);
674 $this->assertEquals('Big City', $address['city']);
675 $this->callAPISuccessGetSingle('Contact', $contactValues);
679 * Test the determination of whether a custom field is valid.
681 public function testCustomFieldValidation() {
683 $customGroup = $this->customGroupCreate([
684 'extends' => 'Contact',
687 $customField = $this->customFieldOptionValueCreate($customGroup, 'fieldABC', ['html_type' => 'Select', 'serialize' => 1]);
689 'custom_' . $customField['id'] => 'Label1|Label2',
691 CRM_Contact_Import_Parser_Contact
::isErrorInCustomData($params, $errorMessage);
692 $this->assertEquals([], $errorMessage);
696 * Test that setting duplicate action to fill doesn't blow away data
697 * that exists, but does fill in where it's empty.
701 public function testImportFill() {
702 // Create a custom field group for testing.
703 $this->createCustomGroup([
704 'title' => 'importFillGroup',
705 'extends' => 'Individual',
708 $customGroupID = $this->ids
['CustomGroup']['importFillGroup'];
710 // Add two custom fields.
712 'custom_group_id' => $customGroupID,
713 'label' => 'importFillField1',
714 'html_type' => 'Select',
715 'data_type' => 'String',
721 $result = $this->callAPISuccess('custom_field', 'create', $api_params);
722 $customField1 = $result['id'];
725 'custom_group_id' => $customGroupID,
726 'label' => 'importFillField2',
727 'html_type' => 'Select',
728 'data_type' => 'String',
734 $result = $this->callAPISuccess('custom_field', 'create', $api_params);
735 $customField2 = $result['id'];
737 // Now set up values.
738 $original_gender = 'Male';
739 $original_custom1 = 'foo';
740 $original_email = 'test-import-fill@example.org';
742 $import_gender = 'Female';
743 $import_custom1 = 'bar';
744 $import_job_title = 'Chief data importer';
745 $import_custom2 = 'baz';
747 // Create contact with both one known core field and one custom
750 'contact_type' => 'Individual',
751 'email' => $original_email,
752 'gender' => $original_gender,
753 'custom_' . $customField1 => $original_custom1,
755 $result = $this->callAPISuccess('contact', 'create', $api_params);
756 $contact_id = $result['id'];
760 'email' => $original_email,
761 'gender_id' => $import_gender,
762 'custom_' . $customField1 => $import_custom1,
763 'job_title' => $import_job_title,
764 'custom_' . $customField2 => $import_custom2,
767 $this->runImport($import, CRM_Import_Parser
::DUPLICATE_FILL
, CRM_Import_Parser
::VALID
);
770 'gender' => $original_gender,
771 'custom_' . $customField1 => $original_custom1,
772 'job_title' => $import_job_title,
773 'custom_' . $customField2 => $import_custom2,
780 'custom_' . $customField1,
782 'custom_' . $customField2,
785 $result = civicrm_api3('Contact', 'get', $params);
786 $values = array_pop($result['values']);
787 foreach ($expected as $field => $expected_value) {
788 if (!isset($values[$field])) {
792 $given_value = $values[$field];
796 // job_title: Chief Data Importer
797 // importFillField1: foo
798 // importFillField2: baz
799 $this->assertEquals($expected_value, $given_value, "$field properly handled during Fill import");
804 * CRM-19888 default country should be used if ambigous.
806 public function testImportAmbiguousStateCountry() {
807 $this->callAPISuccess('Setting', 'create', ['defaultContactCountry' => 1228]);
808 $countries = CRM_Core_PseudoConstant
::country(FALSE, FALSE);
809 $this->callAPISuccess('Setting', 'create', ['countryLimit' => [array_search('United States', $countries), array_search('Guyana', $countries), array_search('Netherlands', $countries)]]);
810 $this->callAPISuccess('Setting', 'create', ['provinceLimit' => [array_search('United States', $countries), array_search('Guyana', $countries), array_search('Netherlands', $countries)]]);
811 $mapper = [0 => NULL, 1 => NULL, 2 => 'Primary', 3 => NULL];
812 list($contactValues) = $this->setUpBaseContact();
813 $fields = array_keys($contactValues);
815 'street_address' => 'PO Box 2716',
817 'state_province' => 'UT',
818 'postal_code' => 84049,
819 'country' => 'United States',
821 $locationTypes = $this->callAPISuccess('Address', 'getoptions', ['field' => 'location_type_id']);
822 $locationTypes = $locationTypes['values'];
823 foreach ($addressValues as $field => $value) {
824 $contactValues['home_' . $field] = $value;
825 $mapper[] = array_search('Home', $locationTypes);
826 $contactValues['work_' . $field] = $value;
827 $mapper[] = array_search('Work', $locationTypes);
831 $contactValues['work_country'] = '';
833 $this->runImport($contactValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
, $mapper, $fields);
834 $addresses = $this->callAPISuccess('Address', 'get', ['contact_id' => ['>' => 2], 'sequential' => 1]);
835 $this->assertEquals(2, $addresses['count']);
836 $this->assertEquals(array_search('United States', $countries), $addresses['values'][0]['country_id']);
837 $this->assertEquals(array_search('United States', $countries), $addresses['values'][1]['country_id']);
841 * Run the import parser.
843 * @param array $originalValues
845 * @param int $onDuplicateAction
846 * @param int $expectedResult
847 * @param array|null $mapperLocType
848 * Array of location types that map to the input arrays.
849 * @param array|null $fields
850 * Array of field names. Will be calculated from $originalValues if not passed in, but
851 * that method does not cope with duplicates.
852 * @param int|null $ruleGroupId
853 * To test against a specific dedupe rule group, pass its ID as this argument.
855 protected function runImport($originalValues, $onDuplicateAction, $expectedResult, $mapperLocType = [], $fields = NULL, int $ruleGroupId = NULL) {
857 $fields = array_keys($originalValues);
859 $values = array_values($originalValues);
860 $parser = new CRM_Contact_Import_Parser_Contact($fields, $mapperLocType);
861 $parser->_contactType
= 'Individual';
862 $parser->_dedupeRuleGroupID
= $ruleGroupId;
863 $parser->_onDuplicate
= $onDuplicateAction;
865 $this->assertEquals($expectedResult, $parser->import($onDuplicateAction, $values), 'Return code from parser import was not as expected');
869 * @param array $fields Array of fields to be imported
870 * @param array $allfields Array of all fields which can be part of import
872 private function mapRelationshipFields(&$fields, $allfields) {
873 foreach ($allfields as $key => $fieldtocheck) {
874 $elementIndex = array_search($fieldtocheck->_title
, $fields);
875 if ($elementIndex !== FALSE) {
876 $fields[$elementIndex] = $key;
882 * Set up the underlying contact.
884 * @param array $params
885 * Optional extra parameters to set.
888 * @throws \CRM_Core_Exception
890 protected function setUpBaseContact($params = []) {
891 $originalValues = array_merge([
892 'first_name' => 'Bill',
893 'last_name' => 'Gates',
894 'email' => 'bill.gates@microsoft.com',
895 'nick_name' => 'Billy-boy',
897 $this->runImport($originalValues, CRM_Import_Parser
::DUPLICATE_UPDATE
, CRM_Import_Parser
::VALID
);
898 $result = $this->callAPISuccessGetSingle('Contact', $originalValues);
899 return [$originalValues, $result];