Merge pull request #14934 from civicrm/5.16
[civicrm-core.git] / tests / phpunit / CRM / Contact / Import / Parser / ContactTest.php
CommitLineData
e87ff4ce 1<?php
2/*
3+--------------------------------------------------------------------+
2fe49090 4| CiviCRM version 5 |
e87ff4ce 5+--------------------------------------------------------------------+
6b83d5bd 6| Copyright CiviCRM LLC (c) 2004-2019 |
e87ff4ce 7+--------------------------------------------------------------------+
8| This file is a part of CiviCRM. |
9| |
10| CiviCRM is free software; you can copy, modify, and distribute it |
11| under the terms of the GNU Affero General Public License |
12| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13| |
14| CiviCRM is distributed in the hope that it will be useful, but |
15| WITHOUT ANY WARRANTY; without even the implied warranty of |
16| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17| See the GNU Affero General Public License for more details. |
18| |
19| You should have received a copy of the GNU Affero General Public |
20| License and the CiviCRM Licensing Exception along |
21| with this program; if not, contact CiviCRM LLC |
22| at info[AT]civicrm[DOT]org. If you have questions about the |
23| GNU Affero General Public License or the licensing of CiviCRM, |
24| see the CiviCRM license FAQ at http://civicrm.org/licensing |
25+--------------------------------------------------------------------+
26 */
27
28/**
29 * @file
30 * File for the CRM_Contact_Imports_Parser_ContactTest class.
31 */
32
e87ff4ce 33/**
34 * Test contact import parser.
35 *
36 * @package CiviCRM
acb109b7 37 * @group headless
e87ff4ce 38 */
91f1e690 39class CRM_Contact_Import_Parser_ContactTest extends CiviUnitTestCase {
2b81c27e 40 use CRMTraits_Custom_CustomDataTrait;
41
42 /**
43 * Main entity for the class.
44 *
45 * @var string
46 */
47 protected $entity = 'Contact';
e87ff4ce 48
49 /**
50 * Setup function.
51 */
52 public function setUp() {
53 parent::setUp();
54 }
55
2b81c27e 56 /**
57 * Tear down after test.
58 *
59 * @throws \CRM_Core_Exception
60 */
61 public function tearDown() {
62 $this->quickCleanup(['civicrm_address', 'civicrm_phone', 'civicrm_email'], TRUE);
63 parent::tearDown();
64 }
65
755a3281
AP
66 /**
67 * Test that import parser will add contact with employee of relationship.
68 *
69 * @throws \Exception
70 */
71 public function testImportParserWtihEmployeeOfRelationship() {
9099cab3 72 $this->organizationCreate([
755a3281
AP
73 "organization_name" => "Agileware",
74 "legal_name" => "Agileware",
9099cab3
CW
75 ]);
76 $contactImportValues = [
755a3281
AP
77 "first_name" => "Alok",
78 "last_name" => "Patel",
79 "Employee of" => "Agileware",
9099cab3 80 ];
755a3281
AP
81
82 $fields = array_keys($contactImportValues);
83 $values = array_values($contactImportValues);
4d847bab 84 $parser = new CRM_Contact_Import_Parser_Contact($fields, []);
755a3281
AP
85 $parser->_contactType = 'Individual';
86 $parser->init();
87 $this->mapRelationshipFields($fields, $parser->getAllFields());
88
9099cab3 89 $parser = new CRM_Contact_Import_Parser_Contact($fields, [], [], [], [
755a3281
AP
90 NULL,
91 NULL,
92 $fields[2],
9099cab3 93 ], [
755a3281
AP
94 NULL,
95 NULL,
96 "Organization",
9099cab3 97 ], [
755a3281
AP
98 NULL,
99 NULL,
100 "organization_name",
9099cab3 101 ], [], [], [], [], []);
755a3281
AP
102
103 $parser->_contactType = 'Individual';
104 $parser->_onDuplicate = CRM_Import_Parser::DUPLICATE_UPDATE;
105 $parser->init();
106
107 $this->assertEquals(CRM_Import_Parser::VALID, $parser->import(CRM_Import_Parser::DUPLICATE_UPDATE, $values), 'Return code from parser import was not as expected');
9099cab3 108 $this->callAPISuccess("Contact", "get", [
39b959db
SL
109 "first_name" => "Alok",
110 "last_name" => "Patel",
111 "organization_name" => "Agileware",
9099cab3 112 ]);
755a3281
AP
113 }
114
13943a50
JM
115 /**
116 * Test that import parser will not fail when same external_identifier found of deleted contact.
117 *
118 * @throws \Exception
119 */
120 public function testImportParserWtihDeletedContactExternalIdentifier() {
9099cab3 121 $contactId = $this->individualCreate([
13943a50 122 "external_identifier" => "ext-1",
9099cab3 123 ]);
13943a50 124 CRM_Contact_BAO_Contact::deleteContact($contactId);
9099cab3 125 list($originalValues, $result) = $this->setUpBaseContact([
13943a50 126 'external_identifier' => 'ext-1',
9099cab3 127 ]);
13943a50
JM
128 $originalValues['nick_name'] = 'Old Bill';
129 $this->runImport($originalValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
130 $originalValues['id'] = $result['id'];
9099cab3 131 $this->assertEquals('ext-1', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'external_identifier']));
13943a50
JM
132 $this->callAPISuccessGetSingle('Contact', $originalValues);
133 }
134
e87ff4ce 135 /**
8fd37b20 136 * Test import parser will update based on a rule match.
137 *
138 * In this case the contact has no external identifier.
e87ff4ce 139 *
140 * @throws \Exception
141 */
142 public function testImportParserWithUpdateWithoutExternalIdentifier() {
8fd37b20 143 list($originalValues, $result) = $this->setUpBaseContact();
e87ff4ce 144 $originalValues['nick_name'] = 'Old Bill';
145 $this->runImport($originalValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
146 $originalValues['id'] = $result['id'];
9099cab3 147 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'nick_name']));
e87ff4ce 148 $this->callAPISuccessGetSingle('Contact', $originalValues);
149 }
150
151 /**
8fd37b20 152 * Test import parser will update contacts with an external identifier.
153 *
154 * This is the basic test where the identifier matches the import parameters.
e87ff4ce 155 *
156 * @throws \Exception
157 */
158 public function testImportParserWithUpdateWithExternalIdentifier() {
9099cab3 159 list($originalValues, $result) = $this->setUpBaseContact(['external_identifier' => 'windows']);
8fd37b20 160
e87ff4ce 161 $this->assertEquals($result['id'], CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', 'windows', 'id', 'external_identifier', TRUE));
162 $this->assertEquals('windows', $result['external_identifier']);
8fd37b20 163
e87ff4ce 164 $originalValues['nick_name'] = 'Old Bill';
165 $this->runImport($originalValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
166 $originalValues['id'] = $result['id'];
8fd37b20 167
9099cab3 168 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'nick_name']));
e87ff4ce 169 $this->callAPISuccessGetSingle('Contact', $originalValues);
170 }
171
65070890 172 /**
173 * Test import parser will fallback to external identifier.
174 *
175 * In this case no primary match exists (e.g the details are not supplied) so it falls back on external identifier.
176 *
177 * CRM-17275
178 *
179 * @throws \Exception
180 */
181 public function testImportParserWithUpdateWithExternalIdentifierButNoPrimaryMatch() {
12e2ccdc 182 list($originalValues, $result) = $this->setUpBaseContact([
65070890 183 'external_identifier' => 'windows',
184 'email' => NULL,
12e2ccdc 185 ]);
65070890 186
187 $this->assertEquals('windows', $result['external_identifier']);
188
189 $originalValues['nick_name'] = 'Old Bill';
190 $this->runImport($originalValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
191 $originalValues['id'] = $result['id'];
192
9099cab3 193 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'nick_name']));
65070890 194 $this->callAPISuccessGetSingle('Contact', $originalValues);
195 }
196
12e2ccdc 197 /**
198 * Test import parser will fallback to external identifier.
199 *
200 * In this case no primary match exists (e.g the details are not supplied) so it falls back on external identifier.
201 *
202 * CRM-17275
203 *
204 * @throws \Exception
205 */
206 public function testImportParserWithUpdateWithContactID() {
9099cab3 207 list($originalValues, $result) = $this->setUpBaseContact([
12e2ccdc 208 'external_identifier' => '',
209 'email' => NULL,
9099cab3 210 ]);
12e2ccdc 211 $updateValues = ['id' => $result['id'], 'email' => 'bill@example.com'];
212 // This is some deep weirdness - this sets a flag for updatingBlankLocinfo - allowing input to be blanked
213 // (which IS a good thing but it's pretty weird & all to do with legacy profile stuff).
214 CRM_Core_Session::singleton()->set('authSrc', CRM_Core_Permission::AUTH_SRC_CHECKSUM);
215 $this->runImport($updateValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, [NULL, 1]);
216 $originalValues['id'] = $result['id'];
217 $this->callAPISuccessGetSingle('Email', ['contact_id' => $originalValues['id'], 'is_primary' => 1]);
218 $this->callAPISuccessGetSingle('Contact', $originalValues);
219 }
220
eb5f7260 221 /**
8fd37b20 222 * Test that the import parser adds the external identifier where none is set.
eb5f7260 223 *
224 * @throws \Exception
225 */
8fd37b20 226 public function testImportParserWithUpdateWithNoExternalIdentifier() {
227 list($originalValues, $result) = $this->setUpBaseContact();
eb5f7260 228 $originalValues['nick_name'] = 'Old Bill';
229 $originalValues['external_identifier'] = 'windows';
230 $this->runImport($originalValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
231 $originalValues['id'] = $result['id'];
9099cab3 232 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'nick_name']));
eb5f7260 233 $this->callAPISuccessGetSingle('Contact', $originalValues);
234 }
235
65070890 236 /**
237 * Test that the import parser changes the external identifier when there is a dedupe match.
238 *
239 * @throws \Exception
240 */
241 public function testImportParserWithUpdateWithChangedExternalIdentifier() {
9099cab3 242 list($contactValues, $result) = $this->setUpBaseContact(['external_identifier' => 'windows']);
65070890 243 $contact_id = $result['id'];
244 $contactValues['nick_name'] = 'Old Bill';
245 $contactValues['external_identifier'] = 'android';
246 $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
247 $contactValues['id'] = $contact_id;
9099cab3 248 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $contact_id, 'return' => 'nick_name']));
65070890 249 $this->callAPISuccessGetSingle('Contact', $contactValues);
250 }
251
10741f35 252 /**
6ebecfea 253 * Test that the import parser adds the address to the right location.
10741f35 254 *
255 * @throws \Exception
256 */
257 public function testImportBillingAddress() {
258 list($contactValues) = $this->setUpBaseContact();
259 $contactValues['nick_name'] = 'Old Bill';
260 $contactValues['external_identifier'] = 'android';
261 $contactValues['street_address'] = 'Big Mansion';
6ebecfea 262 $contactValues['phone'] = '911';
9099cab3
CW
263 $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]);
264 $address = $this->callAPISuccessGetSingle('Address', ['street_address' => 'Big Mansion']);
10741f35 265 $this->assertEquals(2, $address['location_type_id']);
266
9099cab3 267 $phone = $this->callAPISuccessGetSingle('Phone', ['phone' => '911']);
6ebecfea 268 $this->assertEquals(2, $phone['location_type_id']);
269
270 $contact = $this->callAPISuccessGetSingle('Contact', $contactValues);
9099cab3 271 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
6ebecfea 272 }
273
274 /**
275 * Test that the import parser adds the address to the primary location.
276 *
277 * @throws \Exception
278 */
279 public function testImportPrimaryAddress() {
280 list($contactValues) = $this->setUpBaseContact();
281 $contactValues['nick_name'] = 'Old Bill';
282 $contactValues['external_identifier'] = 'android';
283 $contactValues['street_address'] = 'Big Mansion';
284 $contactValues['phone'] = 12334;
9099cab3
CW
285 $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']);
286 $address = $this->callAPISuccessGetSingle('Address', ['street_address' => 'Big Mansion']);
6ebecfea 287 $this->assertEquals(1, $address['location_type_id']);
c77e8e33 288 $this->assertEquals(1, $address['is_primary']);
6ebecfea 289
9099cab3 290 $phone = $this->callAPISuccessGetSingle('Phone', ['phone' => '12334']);
6ebecfea 291 $this->assertEquals(1, $phone['location_type_id']);
292
9099cab3 293 $this->callAPISuccessGetSingle('Email', ['email' => 'bill.gates@microsoft.com']);
2ab10f80 294
6ebecfea 295 $contact = $this->callAPISuccessGetSingle('Contact', $contactValues);
9099cab3 296 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
6ebecfea 297 }
298
be2db205 299 /**
300 * Test that address custom fields can be imported
2d932085 301 * FIXME: Api4
44134d5c 302 *
303 * @throws \CRM_Core_Exception
be2db205 304 */
305 public function testAddressWithCustomData() {
306 $ids = $this->entityCustomGroupWithSingleFieldCreate('Address', 'AddressTest.php');
307 list($contactValues) = $this->setUpBaseContact();
308 $contactValues['nick_name'] = 'Old Bill';
309 $contactValues['external_identifier'] = 'android';
310 $contactValues['street_address'] = 'Big Mansion';
311 $contactValues['custom_' . $ids['custom_field_id']] = 'Update';
9099cab3 312 $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']);
be2db205 313 $address = $this->callAPISuccessGetSingle('Address', ['street_address' => 'Big Mansion', 'return' => 'custom_' . $ids['custom_field_id']]);
314 $this->assertEquals('Update', $address['custom_' . $ids['custom_field_id']]);
315 }
316
44134d5c 317 /**
318 * Test gender works when you specify the label.
319 *
320 * There is an expectation that you can import by label here.
321 *
322 * @throws \CRM_Core_Exception
323 */
324 public function testGenderLabel() {
325 $contactValues = [
326 'first_name' => 'Bill',
327 'last_name' => 'Gates',
328 'email' => 'bill.gates@microsoft.com',
329 'nick_name' => 'Billy-boy',
330 'gender_id' => 'Female',
331 ];
332 $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, [NULL, NULL, 'Primary', NULL, NULL]);
333 $this->callAPISuccessGetSingle('Contact', $contactValues);
334 }
335
2b81c27e 336 /**
337 * Test that labels work for importing custom data.
338 *
339 * @throws \CRM_Core_Exception
340 */
341 public function testCustomDataLabel() {
342 $this->createCustomGroupWithFieldOfType([], 'select');
343 $contactValues = [
344 'first_name' => 'Bill',
345 'last_name' => 'Gates',
346 'email' => 'bill.gates@microsoft.com',
347 'nick_name' => 'Billy-boy',
348 $this->getCustomFieldName('select') => 'Yellow',
349 ];
350 $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, [NULL, NULL, 'Primary', NULL, NULL]);
351 $contact = $this->callAPISuccessGetSingle('Contact', array_merge($contactValues, ['return' => $this->getCustomFieldName('select')]));
352 $this->assertEquals('Y', $contact[$this->getCustomFieldName('select')]);
353 }
354
355 /**
356 * Test that names work for importing custom data.
357 *
358 * @throws \CRM_Core_Exception
359 */
360 public function testCustomDataName() {
361 $this->createCustomGroupWithFieldOfType([], 'select');
362 $contactValues = [
363 'first_name' => 'Bill',
364 'last_name' => 'Gates',
365 'email' => 'bill.gates@microsoft.com',
366 'nick_name' => 'Billy-boy',
367 $this->getCustomFieldName('select') => 'Y',
368 ];
369 $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, [NULL, NULL, 'Primary', NULL, NULL]);
370 $contact = $this->callAPISuccessGetSingle('Contact', array_merge($contactValues, ['return' => $this->getCustomFieldName('select')]));
371 $this->assertEquals('Y', $contact[$this->getCustomFieldName('select')]);
372 }
373
6fdc0e61 374 /**
375 * Test that the import parser adds the address to the primary location.
376 *
377 * @throws \Exception
378 */
379 public function testImportDeceased() {
380 list($contactValues) = $this->setUpBaseContact();
381 CRM_Core_Session::singleton()->set("dateTypes", 1);
382 $contactValues['birth_date'] = '1910-12-17';
383 $contactValues['deceased_date'] = '2010-12-17';
384 $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
385 $contact = $this->callAPISuccessGetSingle('Contact', $contactValues);
386 $this->assertEquals('1910-12-17', $contact['birth_date']);
387 $this->assertEquals('2010-12-17', $contact['deceased_date']);
388 $this->assertEquals(1, $contact['is_deceased']);
9099cab3 389 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
6fdc0e61 390 }
391
6ebecfea 392 /**
393 * Test that the import parser adds the address to the primary location.
394 *
395 * @throws \Exception
396 */
397 public function testImportTwoAddressFirstPrimary() {
398 list($contactValues) = $this->setUpBaseContact();
399 $contactValues['nick_name'] = 'Old Bill';
400 $contactValues['external_identifier'] = 'android';
401 $contactValues['street_address'] = 'Big Mansion';
402 $contactValues['phone'] = 12334;
403 $fields = array_keys($contactValues);
404 $contactValues['street_address_2'] = 'Teeny Mansion';
405 $contactValues['phone_2'] = 4444;
406 $fields[] = 'street_address';
407 $fields[] = 'phone';
9099cab3
CW
408 $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);
409 $contact = $this->callAPISuccessGetSingle('Contact', ['external_identifier' => 'android']);
410 $address = $this->callAPISuccess('Address', 'get', ['contact_id' => $contact['id'], 'sequential' => 1]);
6ebecfea 411
412 $this->assertEquals(3, $address['values'][0]['location_type_id']);
413 $this->assertEquals(0, $address['values'][0]['is_primary']);
414 $this->assertEquals('Teeny Mansion', $address['values'][0]['street_address']);
415
416 $this->assertEquals(1, $address['values'][1]['location_type_id']);
417 $this->assertEquals(1, $address['values'][1]['is_primary']);
418 $this->assertEquals('Big Mansion', $address['values'][1]['street_address']);
419
9099cab3 420 $phone = $this->callAPISuccess('Phone', 'get', ['contact_id' => $contact['id'], 'sequential' => 1]);
6ebecfea 421 $this->assertEquals(1, $phone['values'][0]['location_type_id']);
422 $this->assertEquals(1, $phone['values'][0]['is_primary']);
423 $this->assertEquals(12334, $phone['values'][0]['phone']);
424 $this->assertEquals(3, $phone['values'][1]['location_type_id']);
425 $this->assertEquals(0, $phone['values'][1]['is_primary']);
426 $this->assertEquals(4444, $phone['values'][1]['phone']);
427
9099cab3 428 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
6ebecfea 429 }
430
431 /**
432 * Test that the import parser adds the address to the primary location.
433 *
434 * @throws \Exception
435 */
436 public function testImportTwoAddressSecondPrimary() {
437 list($contactValues) = $this->setUpBaseContact();
438 $contactValues['nick_name'] = 'Old Bill';
439 $contactValues['external_identifier'] = 'android';
440 $contactValues['street_address'] = 'Big Mansion';
441 $contactValues['phone'] = 12334;
442 $fields = array_keys($contactValues);
443 $contactValues['street_address_2'] = 'Teeny Mansion';
444 $contactValues['phone_2'] = 4444;
445 $fields[] = 'street_address';
446 $fields[] = 'phone';
9099cab3
CW
447 $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);
448 $contact = $this->callAPISuccessGetSingle('Contact', ['external_identifier' => 'android']);
449 $address = $this->callAPISuccess('Address', 'get', ['contact_id' => $contact['id'], 'sequential' => 1])['values'];
6ebecfea 450
91f1e690 451 $this->assertEquals(1, $address[1]['location_type_id']);
452 $this->assertEquals(1, $address[1]['is_primary']);
453 $this->assertEquals('Teeny Mansion', $address[1]['street_address']);
6ebecfea 454
91f1e690 455 $this->assertEquals(3, $address[0]['location_type_id']);
456 $this->assertEquals(0, $address[0]['is_primary']);
457 $this->assertEquals('Big Mansion', $address[0]['street_address']);
6ebecfea 458
2ab10f80 459 $phone = $this->callAPISuccess('Phone', 'get', ['contact_id' => $contact['id'], 'sequential' => 1, 'options' => ['sort' => 'is_primary DESC']])['values'];
91f1e690 460 $this->assertEquals(3, $phone[1]['location_type_id']);
461 $this->assertEquals(0, $phone[1]['is_primary']);
462 $this->assertEquals(12334, $phone[1]['phone']);
463 $this->assertEquals(1, $phone[0]['location_type_id']);
464 $this->assertEquals(1, $phone[0]['is_primary']);
465 $this->assertEquals(4444, $phone[0]['phone']);
6ebecfea 466
9099cab3 467 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
6ebecfea 468 }
469
470 /**
471 * Test that the import parser updates the address on the existing primary location.
472 *
473 * @throws \Exception
474 */
475 public function testImportPrimaryAddressUpdate() {
9099cab3 476 list($contactValues) = $this->setUpBaseContact(['external_identifier' => 'android']);
2ab10f80 477 $contactValues['email'] = 'melinda.gates@microsoft.com';
478 $contactValues['phone'] = '98765';
6ebecfea 479 $contactValues['external_identifier'] = 'android';
480 $contactValues['street_address'] = 'Big Mansion';
c77e8e33 481 $contactValues['city'] = 'Big City';
9099cab3
CW
482 $contactID = $this->callAPISuccessGetValue('Contact', ['external_identifier' => 'android', 'return' => 'id']);
483 $originalAddress = $this->callAPISuccess('Address', 'create', ['location_type_id' => 2, 'street_address' => 'small house', 'contact_id' => $contactID]);
484 $originalPhone = $this->callAPISuccess('phone', 'create', ['location_type_id' => 2, 'phone' => '1234', 'contact_id' => $contactID]);
485 $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']);
2ab10f80 486 $phone = $this->callAPISuccessGetSingle('Phone', ['phone' => '98765']);
487 $this->assertEquals(2, $phone['location_type_id']);
488 $this->assertEquals($originalPhone['id'], $phone['id']);
489 $email = $this->callAPISuccess('Email', 'getsingle', ['contact_id' => $contactID]);
9099cab3 490 $address = $this->callAPISuccessGetSingle('Address', ['street_address' => 'Big Mansion']);
6ebecfea 491 $this->assertEquals(2, $address['location_type_id']);
492 $this->assertEquals($originalAddress['id'], $address['id']);
c77e8e33 493 $this->assertEquals('Big City', $address['city']);
10741f35 494 $this->callAPISuccessGetSingle('Contact', $contactValues);
495 }
496
13943a50
JM
497 /**
498 * Test the determination of whether a custom field is valid.
499 */
412585fb 500 public function testCustomFieldValidation() {
9099cab3
CW
501 $errorMessage = [];
502 $customGroup = $this->customGroupCreate([
3b2c326e 503 'extends' => 'Contact',
f1230f06 504 'title' => 'ABC',
9099cab3
CW
505 ]);
506 $customField = $this->customFieldOptionValueCreate($customGroup, 'fieldABC', ['html_type' => 'Multi-Select']);
507 $params = [
412585fb 508 'custom_' . $customField['id'] => 'Label1|Label2',
9099cab3 509 ];
412585fb 510 CRM_Contact_Import_Parser_Contact::isErrorInCustomData($params, $errorMessage);
9099cab3 511 $this->assertEquals([], $errorMessage);
412585fb 512 }
513
6e975197
JM
514 /**
515 * Test that setting duplicate action to fill doesn't blow away data
516 * that exists, but does fill in where it's empty.
517 *
518 * @throw \Exception
519 */
520 public function testImportFill() {
521 // Create a custom field group for testing.
a0c6165f 522 $this->createCustomGroup([
523 'title' => 'importFillGroup',
524 'extends' => 'Individual',
525 'is_active' => TRUE,
526 ]);
527 $customGroupID = $this->ids['CustomGroup']['importFillGroup'];
6e975197
JM
528
529 // Add two custom fields.
9099cab3 530 $api_params = [
a0c6165f 531 'custom_group_id' => $customGroupID,
6e975197
JM
532 'label' => 'importFillField1',
533 'html_type' => 'Select',
534 'data_type' => 'String',
9099cab3 535 'option_values' => [
6e975197
JM
536 'foo' => 'Foo',
537 'bar' => 'Bar',
9099cab3
CW
538 ],
539 ];
6e975197
JM
540 $result = $this->callAPISuccess('custom_field', 'create', $api_params);
541 $customField1 = $result['id'];
542
9099cab3 543 $api_params = [
a0c6165f 544 'custom_group_id' => $customGroupID,
6e975197
JM
545 'label' => 'importFillField2',
546 'html_type' => 'Select',
547 'data_type' => 'String',
9099cab3 548 'option_values' => [
6e975197
JM
549 'baz' => 'Baz',
550 'boo' => 'Boo',
9099cab3
CW
551 ],
552 ];
6e975197
JM
553 $result = $this->callAPISuccess('custom_field', 'create', $api_params);
554 $customField2 = $result['id'];
555
556 // Now set up values.
557 $original_gender = 'Male';
558 $original_custom1 = 'foo';
6e975197
JM
559 $original_email = 'test-import-fill@example.org';
560
561 $import_gender = 'Female';
562 $import_custom1 = 'bar';
563 $import_job_title = 'Chief data importer';
564 $import_custom2 = 'baz';
565
566 // Create contact with both one known core field and one custom
567 // field filled in.
9099cab3 568 $api_params = [
6e975197
JM
569 'contact_type' => 'Individual',
570 'email' => $original_email,
571 'gender' => $original_gender,
f3cfbe94 572 'custom_' . $customField1 => $original_custom1,
9099cab3 573 ];
6e975197
JM
574 $result = $this->callAPISuccess('contact', 'create', $api_params);
575 $contact_id = $result['id'];
576
577 // Run an import.
9099cab3 578 $import = [
6e975197
JM
579 'email' => $original_email,
580 'gender_id' => $import_gender,
581 'custom_' . $customField1 => $import_custom1,
582 'job_title' => $import_job_title,
f3cfbe94 583 'custom_' . $customField2 => $import_custom2,
9099cab3 584 ];
6e975197
JM
585
586 $this->runImport($import, CRM_Import_Parser::DUPLICATE_FILL, CRM_Import_Parser::VALID);
587
9099cab3 588 $expected = [
6e975197
JM
589 'gender' => $original_gender,
590 'custom_' . $customField1 => $original_custom1,
591 'job_title' => $import_job_title,
f3cfbe94 592 'custom_' . $customField2 => $import_custom2,
9099cab3 593 ];
6e975197 594
9099cab3 595 $params = [
6e975197 596 'id' => $contact_id,
9099cab3 597 'return' => [
6e975197
JM
598 'gender',
599 'custom_' . $customField1,
600 'job_title',
f3cfbe94 601 'custom_' . $customField2,
9099cab3
CW
602 ],
603 ];
6e975197
JM
604 $result = civicrm_api3('Contact', 'get', $params);
605 $values = array_pop($result['values']);
f3cfbe94 606 foreach ($expected as $field => $expected_value) {
6e975197 607 if (!isset($values[$field])) {
f3cfbe94 608 $given_value = NULL;
6e975197
JM
609 }
610 else {
611 $given_value = $values[$field];
612 }
613 // We expect:
614 // gender: Male
615 // job_title: Chief Data Importer
616 // importFillField1: foo
617 // importFillField2: baz
618 $this->assertEquals($expected_value, $given_value, "$field properly handled during Fill import");
619 }
620 }
621
4352bd72 622 /**
623 * CRM-19888 default country should be used if ambigous.
624 */
625 public function testImportAmbiguousStateCountry() {
1c8738dd 626 $this->callAPISuccess('Setting', 'create', ['defaultContactCountry' => 1228]);
4352bd72 627 $countries = CRM_Core_PseudoConstant::country(FALSE, FALSE);
9099cab3
CW
628 $this->callAPISuccess('Setting', 'create', ['countryLimit' => [array_search('United States', $countries), array_search('Guyana', $countries), array_search('Netherlands', $countries)]]);
629 $this->callAPISuccess('Setting', 'create', ['provinceLimit' => [array_search('United States', $countries), array_search('Guyana', $countries), array_search('Netherlands', $countries)]]);
630 $mapper = [0 => NULL, 1 => NULL, 2 => 'Primary', 3 => NULL];
4352bd72 631 list($contactValues) = $this->setUpBaseContact();
632 $fields = array_keys($contactValues);
9099cab3 633 $addressValues = [
4352bd72 634 'street_address' => 'PO Box 2716',
635 'city' => 'Midway',
636 'state_province' => 'UT',
637 'postal_code' => 84049,
638 'country' => 'United States',
9099cab3
CW
639 ];
640 $locationTypes = $this->callAPISuccess('Address', 'getoptions', ['field' => 'location_type_id']);
4352bd72 641 $locationTypes = $locationTypes['values'];
642 foreach ($addressValues as $field => $value) {
643 $contactValues['home_' . $field] = $value;
644 $mapper[] = array_search('Home', $locationTypes);
645 $contactValues['work_' . $field] = $value;
646 $mapper[] = array_search('Work', $locationTypes);
647 $fields[] = $field;
648 $fields[] = $field;
649 }
650 $contactValues['work_country'] = '';
651
652 $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, $mapper, $fields);
9099cab3 653 $addresses = $this->callAPISuccess('Address', 'get', ['contact_id' => ['>' => 2], 'sequential' => 1]);
4352bd72 654 $this->assertEquals(2, $addresses['count']);
655 $this->assertEquals(array_search('United States', $countries), $addresses['values'][0]['country_id']);
656 $this->assertEquals(array_search('United States', $countries), $addresses['values'][1]['country_id']);
657 }
658
e87ff4ce 659 /**
660 * Run the import parser.
661 *
662 * @param array $originalValues
663 *
664 * @param int $onDuplicateAction
665 * @param int $expectedResult
10741f35 666 * @param array|null $mapperLocType
91f1e690 667 * Array of location types that map to the input arrays.
6ebecfea 668 * @param array|null $fields
669 * Array of field names. Will be calculated from $originalValues if not passed in, but
670 * that method does not cope with duplicates.
e87ff4ce 671 */
91f1e690 672 protected function runImport($originalValues, $onDuplicateAction, $expectedResult, $mapperLocType = [], $fields = NULL) {
6ebecfea 673 if (!$fields) {
674 $fields = array_keys($originalValues);
675 }
e87ff4ce 676 $values = array_values($originalValues);
10741f35 677 $parser = new CRM_Contact_Import_Parser_Contact($fields, $mapperLocType);
e87ff4ce 678 $parser->_contactType = 'Individual';
679 $parser->_onDuplicate = $onDuplicateAction;
680 $parser->init();
65070890 681 $this->assertEquals($expectedResult, $parser->import($onDuplicateAction, $values), 'Return code from parser import was not as expected');
e87ff4ce 682 }
ffe87781 683
755a3281
AP
684 /**
685 * @param array $fields Array of fields to be imported
686 * @param array $allfields Array of all fields which can be part of import
687 */
688 private function mapRelationshipFields(&$fields, $allfields) {
689 foreach ($allfields as $key => $fieldtocheck) {
690 $elementIndex = array_search($fieldtocheck->_title, $fields);
691 if ($elementIndex !== FALSE) {
692 $fields[$elementIndex] = $key;
693 }
694 }
695 }
696
8fd37b20 697 /**
698 * Set up the underlying contact.
699 *
700 * @param array $params
701 * Optional extra parameters to set.
702 *
703 * @return array
44134d5c 704 * @throws \CRM_Core_Exception
8fd37b20 705 */
9099cab3
CW
706 protected function setUpBaseContact($params = []) {
707 $originalValues = array_merge([
8fd37b20 708 'first_name' => 'Bill',
709 'last_name' => 'Gates',
710 'email' => 'bill.gates@microsoft.com',
711 'nick_name' => 'Billy-boy',
9099cab3 712 ], $params);
8fd37b20 713 $this->runImport($originalValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
714 $result = $this->callAPISuccessGetSingle('Contact', $originalValues);
9099cab3 715 return [$originalValues, $result];
8fd37b20 716 }
717
e87ff4ce 718}