Fix for cust field
[civicrm-core.git] / tests / phpunit / CRM / Contact / Import / Parser / ContactTest.php
CommitLineData
e87ff4ce 1<?php
2/*
7d61e75f
TO
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 +--------------------------------------------------------------------+
e87ff4ce 10 */
11
12/**
13 * @file
14 * File for the CRM_Contact_Imports_Parser_ContactTest class.
15 */
16
e87ff4ce 17/**
18 * Test contact import parser.
19 *
20 * @package CiviCRM
acb109b7 21 * @group headless
e87ff4ce 22 */
91f1e690 23class CRM_Contact_Import_Parser_ContactTest extends CiviUnitTestCase {
2b81c27e 24 use CRMTraits_Custom_CustomDataTrait;
25
26 /**
27 * Main entity for the class.
28 *
29 * @var string
30 */
31 protected $entity = 'Contact';
e87ff4ce 32
2b81c27e 33 /**
34 * Tear down after test.
35 *
36 * @throws \CRM_Core_Exception
37 */
594a9328 38 public function tearDown(): void {
2b81c27e 39 $this->quickCleanup(['civicrm_address', 'civicrm_phone', 'civicrm_email'], TRUE);
40 parent::tearDown();
41 }
42
755a3281
AP
43 /**
44 * Test that import parser will add contact with employee of relationship.
755a3281 45 */
eba173d6 46 public function testImportParserWithEmployeeOfRelationship(): void {
9099cab3 47 $this->organizationCreate([
eba173d6 48 'organization_name' => 'Agileware',
49 'legal_name' => 'Agileware',
9099cab3
CW
50 ]);
51 $contactImportValues = [
755a3281
AP
52 "first_name" => "Alok",
53 "last_name" => "Patel",
54 "Employee of" => "Agileware",
9099cab3 55 ];
755a3281
AP
56
57 $fields = array_keys($contactImportValues);
58 $values = array_values($contactImportValues);
4d847bab 59 $parser = new CRM_Contact_Import_Parser_Contact($fields, []);
755a3281
AP
60 $parser->_contactType = 'Individual';
61 $parser->init();
62 $this->mapRelationshipFields($fields, $parser->getAllFields());
63
9099cab3 64 $parser = new CRM_Contact_Import_Parser_Contact($fields, [], [], [], [
755a3281
AP
65 NULL,
66 NULL,
67 $fields[2],
9099cab3 68 ], [
755a3281
AP
69 NULL,
70 NULL,
71 "Organization",
9099cab3 72 ], [
755a3281
AP
73 NULL,
74 NULL,
75 "organization_name",
9099cab3 76 ], [], [], [], [], []);
755a3281
AP
77
78 $parser->_contactType = 'Individual';
79 $parser->_onDuplicate = CRM_Import_Parser::DUPLICATE_UPDATE;
80 $parser->init();
81
82 $this->assertEquals(CRM_Import_Parser::VALID, $parser->import(CRM_Import_Parser::DUPLICATE_UPDATE, $values), 'Return code from parser import was not as expected');
9099cab3 83 $this->callAPISuccess("Contact", "get", [
39b959db
SL
84 "first_name" => "Alok",
85 "last_name" => "Patel",
86 "organization_name" => "Agileware",
9099cab3 87 ]);
755a3281
AP
88 }
89
13943a50 90 /**
a8ea3922 91 * Test that import parser will not fail when same external_identifier found
92 * of deleted contact.
13943a50 93 *
c6a3c749 94 * @throws \CRM_Core_Exception
a8ea3922 95 * @throws \CiviCRM_API3_Exception
13943a50 96 */
a8ea3922 97 public function testImportParserWithDeletedContactExternalIdentifier(): void {
9099cab3 98 $contactId = $this->individualCreate([
c6a3c749 99 'external_identifier' => 'ext-1',
9099cab3 100 ]);
c6a3c749 101 $this->callAPISuccess('Contact', 'delete', ['id' => $contactId]);
a8ea3922 102 [$originalValues, $result] = $this->setUpBaseContact([
13943a50 103 'external_identifier' => 'ext-1',
9099cab3 104 ]);
13943a50
JM
105 $originalValues['nick_name'] = 'Old Bill';
106 $this->runImport($originalValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
107 $originalValues['id'] = $result['id'];
9099cab3 108 $this->assertEquals('ext-1', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'external_identifier']));
13943a50
JM
109 $this->callAPISuccessGetSingle('Contact', $originalValues);
110 }
111
e87ff4ce 112 /**
8fd37b20 113 * Test import parser will update based on a rule match.
114 *
115 * In this case the contact has no external identifier.
e87ff4ce 116 *
697f7b54 117 * @throws \CRM_Core_Exception
e87ff4ce 118 */
697f7b54 119 public function testImportParserWithUpdateWithoutExternalIdentifier(): void {
120 [$originalValues, $result] = $this->setUpBaseContact();
e87ff4ce 121 $originalValues['nick_name'] = 'Old Bill';
122 $this->runImport($originalValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
123 $originalValues['id'] = $result['id'];
9099cab3 124 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'nick_name']));
e87ff4ce 125 $this->callAPISuccessGetSingle('Contact', $originalValues);
126 }
127
128 /**
8fd37b20 129 * Test import parser will update contacts with an external identifier.
130 *
131 * This is the basic test where the identifier matches the import parameters.
e87ff4ce 132 *
133 * @throws \Exception
134 */
135 public function testImportParserWithUpdateWithExternalIdentifier() {
eba173d6 136 [$originalValues, $result] = $this->setUpBaseContact(['external_identifier' => 'windows']);
8fd37b20 137
e87ff4ce 138 $this->assertEquals($result['id'], CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', 'windows', 'id', 'external_identifier', TRUE));
139 $this->assertEquals('windows', $result['external_identifier']);
8fd37b20 140
e87ff4ce 141 $originalValues['nick_name'] = 'Old Bill';
142 $this->runImport($originalValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
143 $originalValues['id'] = $result['id'];
8fd37b20 144
9099cab3 145 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'nick_name']));
e87ff4ce 146 $this->callAPISuccessGetSingle('Contact', $originalValues);
147 }
148
65070890 149 /**
150 * Test import parser will fallback to external identifier.
151 *
152 * In this case no primary match exists (e.g the details are not supplied) so it falls back on external identifier.
153 *
0e480632 154 * @see https://issues.civicrm.org/jira/browse/CRM-17275
65070890 155 *
156 * @throws \Exception
157 */
158 public function testImportParserWithUpdateWithExternalIdentifierButNoPrimaryMatch() {
eba173d6 159 [$originalValues, $result] = $this->setUpBaseContact([
65070890 160 'external_identifier' => 'windows',
161 'email' => NULL,
12e2ccdc 162 ]);
65070890 163
164 $this->assertEquals('windows', $result['external_identifier']);
165
166 $originalValues['nick_name'] = 'Old Bill';
167 $this->runImport($originalValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
168 $originalValues['id'] = $result['id'];
169
9099cab3 170 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'nick_name']));
65070890 171 $this->callAPISuccessGetSingle('Contact', $originalValues);
172 }
173
12e2ccdc 174 /**
175 * Test import parser will fallback to external identifier.
176 *
177 * In this case no primary match exists (e.g the details are not supplied) so it falls back on external identifier.
178 *
0e480632 179 * @see https://issues.civicrm.org/jira/browse/CRM-17275
12e2ccdc 180 *
181 * @throws \Exception
182 */
183 public function testImportParserWithUpdateWithContactID() {
eba173d6 184 [$originalValues, $result] = $this->setUpBaseContact([
12e2ccdc 185 'external_identifier' => '',
186 'email' => NULL,
9099cab3 187 ]);
12e2ccdc 188 $updateValues = ['id' => $result['id'], 'email' => 'bill@example.com'];
189 // This is some deep weirdness - this sets a flag for updatingBlankLocinfo - allowing input to be blanked
190 // (which IS a good thing but it's pretty weird & all to do with legacy profile stuff).
191 CRM_Core_Session::singleton()->set('authSrc', CRM_Core_Permission::AUTH_SRC_CHECKSUM);
192 $this->runImport($updateValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, [NULL, 1]);
193 $originalValues['id'] = $result['id'];
194 $this->callAPISuccessGetSingle('Email', ['contact_id' => $originalValues['id'], 'is_primary' => 1]);
195 $this->callAPISuccessGetSingle('Contact', $originalValues);
196 }
197
eb5f7260 198 /**
8fd37b20 199 * Test that the import parser adds the external identifier where none is set.
eb5f7260 200 *
201 * @throws \Exception
202 */
8fd37b20 203 public function testImportParserWithUpdateWithNoExternalIdentifier() {
eba173d6 204 [$originalValues, $result] = $this->setUpBaseContact();
eb5f7260 205 $originalValues['nick_name'] = 'Old Bill';
206 $originalValues['external_identifier'] = 'windows';
207 $this->runImport($originalValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
208 $originalValues['id'] = $result['id'];
9099cab3 209 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $result['id'], 'return' => 'nick_name']));
eb5f7260 210 $this->callAPISuccessGetSingle('Contact', $originalValues);
211 }
212
65070890 213 /**
214 * Test that the import parser changes the external identifier when there is a dedupe match.
215 *
216 * @throws \Exception
217 */
218 public function testImportParserWithUpdateWithChangedExternalIdentifier() {
eba173d6 219 [$contactValues, $result] = $this->setUpBaseContact(['external_identifier' => 'windows']);
65070890 220 $contact_id = $result['id'];
221 $contactValues['nick_name'] = 'Old Bill';
222 $contactValues['external_identifier'] = 'android';
223 $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
224 $contactValues['id'] = $contact_id;
9099cab3 225 $this->assertEquals('Old Bill', $this->callAPISuccessGetValue('Contact', ['id' => $contact_id, 'return' => 'nick_name']));
65070890 226 $this->callAPISuccessGetSingle('Contact', $contactValues);
227 }
228
10741f35 229 /**
6ebecfea 230 * Test that the import parser adds the address to the right location.
10741f35 231 *
232 * @throws \Exception
233 */
234 public function testImportBillingAddress() {
eba173d6 235 [$contactValues] = $this->setUpBaseContact();
10741f35 236 $contactValues['nick_name'] = 'Old Bill';
237 $contactValues['external_identifier'] = 'android';
238 $contactValues['street_address'] = 'Big Mansion';
6ebecfea 239 $contactValues['phone'] = '911';
9099cab3
CW
240 $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]);
241 $address = $this->callAPISuccessGetSingle('Address', ['street_address' => 'Big Mansion']);
10741f35 242 $this->assertEquals(2, $address['location_type_id']);
243
9099cab3 244 $phone = $this->callAPISuccessGetSingle('Phone', ['phone' => '911']);
6ebecfea 245 $this->assertEquals(2, $phone['location_type_id']);
246
247 $contact = $this->callAPISuccessGetSingle('Contact', $contactValues);
9099cab3 248 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
6ebecfea 249 }
250
5e559a23 251 /**
252 * Test that the not-really-encouraged way of creating locations via contact.create doesn't mess up primaries.
253 */
254 public function testContactLocationBlockHandling() {
255 $id = $this->individualCreate([
256 'phone' => [
257 1 => [
258 'location_type_id' => 1,
259 'phone' => '987654321',
260 ],
261 2 => [
262 'location_type_id' => 2,
263 'phone' => '456-7890',
264 ],
265 ],
266 'im' => [
267 1 => [
268 'location_type_id' => 1,
269 'name' => 'bob',
270 ],
271 2 => [
272 'location_type_id' => 2,
273 'name' => 'fred',
274 ],
275 ],
276 'openid' => [
277 1 => [
278 'location_type_id' => 1,
279 'openid' => 'bob',
280 ],
281 2 => [
282 'location_type_id' => 2,
283 'openid' => 'fred',
284 ],
285 ],
286 'email' => [
287 1 => [
288 'location_type_id' => 1,
289 'email' => 'bob@example.com',
290 ],
291 2 => [
292 'location_type_id' => 2,
293 'email' => 'fred@example.com',
294 ],
295 ],
296 ]);
297 $phones = $this->callAPISuccess('Phone', 'get', ['contact_id' => $id])['values'];
298 $emails = $this->callAPISuccess('Email', 'get', ['contact_id' => $id])['values'];
299 $openIDs = $this->callAPISuccess('OpenID', 'get', ['contact_id' => $id])['values'];
300 $ims = $this->callAPISuccess('IM', 'get', ['contact_id' => $id])['values'];
301 $this->assertCount(2, $phones);
302 $this->assertCount(2, $emails);
303 $this->assertCount(2, $ims);
304 $this->assertCount(2, $openIDs);
305
306 $this->assertLocationValidity();
307 $this->callAPISuccess('Contact', 'create', [
308 'id' => $id,
309 // This is secret code for 'delete this phone'.
310 'updateBlankLocInfo' => TRUE,
311 'phone' => [
312 1 => [
313 'id' => key($phones),
314 ],
315 ],
316 'email' => [
317 1 => [
318 'id' => key($emails),
319 ],
320 ],
321 'im' => [
322 1 => [
323 'id' => key($ims),
324 ],
325 ],
326 'openid' => [
327 1 => [
328 'id' => key($openIDs),
329 ],
330 ],
331 ]);
332 $this->assertLocationValidity();
333 $this->callAPISuccessGetCount('Phone', ['contact_id' => $id], 1);
334 $this->callAPISuccessGetCount('Email', ['contact_id' => $id], 1);
335 $this->callAPISuccessGetCount('OpenID', ['contact_id' => $id], 1);
336 $this->callAPISuccessGetCount('IM', ['contact_id' => $id], 1);
337 }
338
6ebecfea 339 /**
340 * Test that the import parser adds the address to the primary location.
341 *
342 * @throws \Exception
343 */
344 public function testImportPrimaryAddress() {
eba173d6 345 [$contactValues] = $this->setUpBaseContact();
6ebecfea 346 $contactValues['nick_name'] = 'Old Bill';
347 $contactValues['external_identifier'] = 'android';
348 $contactValues['street_address'] = 'Big Mansion';
349 $contactValues['phone'] = 12334;
9099cab3
CW
350 $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']);
351 $address = $this->callAPISuccessGetSingle('Address', ['street_address' => 'Big Mansion']);
6ebecfea 352 $this->assertEquals(1, $address['location_type_id']);
c77e8e33 353 $this->assertEquals(1, $address['is_primary']);
6ebecfea 354
9099cab3 355 $phone = $this->callAPISuccessGetSingle('Phone', ['phone' => '12334']);
6ebecfea 356 $this->assertEquals(1, $phone['location_type_id']);
357
9099cab3 358 $this->callAPISuccessGetSingle('Email', ['email' => 'bill.gates@microsoft.com']);
2ab10f80 359
6ebecfea 360 $contact = $this->callAPISuccessGetSingle('Contact', $contactValues);
9099cab3 361 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
6ebecfea 362 }
363
b4e15ec2
JG
364 /**
365 * Test that address location type id is ignored for dedupe purposes on import.
366 *
367 * @throws \Exception
368 */
369 public function testIgnoreLocationTypeId() {
370 // Create a rule that matches on last name and street address.
371 $rgid = $this->createRuleGroup()['id'];
372 $this->callAPISuccess('Rule', 'create', [
373 'dedupe_rule_group_id' => $rgid,
374 'rule_field' => 'last_name',
375 'rule_table' => 'civicrm_contact',
376 'rule_weight' => 4,
377 ]);
378 $this->callAPISuccess('Rule', 'create', [
379 'dedupe_rule_group_id' => $rgid,
380 'rule_field' => 'street_address',
381 'rule_table' => 'civicrm_address',
382 'rule_weight' => 4,
383 ]);
384 // Create a contact with an address of location_type_id 1.
385 $contact1Params = [
386 'contact_type' => 'Individual',
387 'first_name' => 'Original',
388 'last_name' => 'Smith',
389 ];
390 $contact1 = $this->callAPISuccess('Contact', 'create', $contact1Params);
391 $this->callAPISuccess('Address', 'create', [
392 'contact_id' => $contact1['id'],
393 'location_type_id' => 1,
394 'street_address' => 'Big Mansion',
395 ]);
396
397 $contactValues = [
398 'first_name' => 'New',
399 'last_name' => 'Smith',
400 'street_address' => 'Big Mansion',
401 ];
402
403 // We want to import with a location_type_id of 4.
404 $importLocationTypeId = '4';
405 $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_SKIP, CRM_Import_Parser::DUPLICATE, [0 => NULL, 1 => NULL, 2 => $importLocationTypeId], NULL, $rgid);
406 $address = $this->callAPISuccessGetSingle('Address', ['street_address' => 'Big Mansion']);
407 $this->assertEquals(1, $address['location_type_id']);
408 $contact = $this->callAPISuccessGetSingle('Contact', $contact1Params);
409 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
410 }
411
be2db205 412 /**
413 * Test that address custom fields can be imported
2d932085 414 * FIXME: Api4
44134d5c 415 *
416 * @throws \CRM_Core_Exception
be2db205 417 */
418 public function testAddressWithCustomData() {
419 $ids = $this->entityCustomGroupWithSingleFieldCreate('Address', 'AddressTest.php');
eba173d6 420 [$contactValues] = $this->setUpBaseContact();
be2db205 421 $contactValues['nick_name'] = 'Old Bill';
422 $contactValues['external_identifier'] = 'android';
423 $contactValues['street_address'] = 'Big Mansion';
424 $contactValues['custom_' . $ids['custom_field_id']] = 'Update';
9099cab3 425 $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 426 $address = $this->callAPISuccessGetSingle('Address', ['street_address' => 'Big Mansion', 'return' => 'custom_' . $ids['custom_field_id']]);
427 $this->assertEquals('Update', $address['custom_' . $ids['custom_field_id']]);
428 }
429
44134d5c 430 /**
431 * Test gender works when you specify the label.
432 *
433 * There is an expectation that you can import by label here.
434 *
435 * @throws \CRM_Core_Exception
436 */
437 public function testGenderLabel() {
438 $contactValues = [
439 'first_name' => 'Bill',
440 'last_name' => 'Gates',
441 'email' => 'bill.gates@microsoft.com',
442 'nick_name' => 'Billy-boy',
443 'gender_id' => 'Female',
444 ];
445 $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, [NULL, NULL, 'Primary', NULL, NULL]);
446 $this->callAPISuccessGetSingle('Contact', $contactValues);
447 }
448
0b0285b1 449 /**
450 * Test prefix & suffix work when you specify the label.
451 *
452 * There is an expectation that you can import by label here.
453 *
454 * @throws \CRM_Core_Exception
455 * @throws \CiviCRM_API3_Exception
456 */
cdfa6649 457 public function testPrefixLabel(): void {
0b0285b1 458 $this->callAPISuccess('OptionValue', 'create', ['option_group_id' => 'individual_prefix', 'name' => 'new_one', 'label' => 'special', 'value' => 70]);
459 $mapping = [
cd41fa5b 460 ['name' => 'first_name', 'column_number' => 0],
461 ['name' => 'last_name', 'column_number' => 1],
462 ['name' => 'email', 'column_number' => 2, 'location_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Email', 'location_type_id', 'Home')],
463 ['name' => 'prefix_id', 'column_number' => 3],
0b0285b1 464 ['name' => 'suffix_id', 'column_number' => 4],
465 ];
466 $processor = new CRM_Import_ImportProcessor();
467 $processor->setMappingFields($mapping);
468 $processor->setContactType('Individual');
469 $importer = $processor->getImporterObject();
470
471 $contactValues = [
472 'Bill',
473 'Gates',
474 'bill.gates@microsoft.com',
0b0285b1 475 'special',
cd41fa5b 476 'III',
0b0285b1 477 ];
478 $importer->import(CRM_Import_Parser::DUPLICATE_NOCHECK, $contactValues);
479
480 $contact = $this->callAPISuccessGetSingle('Contact', ['first_name' => 'Bill', 'prefix_id' => 'new_one', 'suffix_id' => 'III']);
cd41fa5b 481 $this->assertEquals('special Bill Gates III', $contact['display_name']);
0b0285b1 482 }
483
2b81c27e 484 /**
485 * Test that labels work for importing custom data.
486 *
cdfa6649 487 * @throws \API_Exception
2b81c27e 488 * @throws \CRM_Core_Exception
cdfa6649 489 * @throws \CiviCRM_API3_Exception
2b81c27e 490 */
cdfa6649 491 public function testCustomDataLabel(): void {
2b81c27e 492 $this->createCustomGroupWithFieldOfType([], 'select');
493 $contactValues = [
494 'first_name' => 'Bill',
495 'last_name' => 'Gates',
496 'email' => 'bill.gates@microsoft.com',
497 'nick_name' => 'Billy-boy',
498 $this->getCustomFieldName('select') => 'Yellow',
499 ];
500 $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, [NULL, NULL, 'Primary', NULL, NULL]);
501 $contact = $this->callAPISuccessGetSingle('Contact', array_merge($contactValues, ['return' => $this->getCustomFieldName('select')]));
502 $this->assertEquals('Y', $contact[$this->getCustomFieldName('select')]);
503 }
504
505 /**
506 * Test that names work for importing custom data.
507 *
508 * @throws \CRM_Core_Exception
509 */
510 public function testCustomDataName() {
511 $this->createCustomGroupWithFieldOfType([], 'select');
512 $contactValues = [
513 'first_name' => 'Bill',
514 'last_name' => 'Gates',
515 'email' => 'bill.gates@microsoft.com',
516 'nick_name' => 'Billy-boy',
517 $this->getCustomFieldName('select') => 'Y',
518 ];
519 $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, [NULL, NULL, 'Primary', NULL, NULL]);
520 $contact = $this->callAPISuccessGetSingle('Contact', array_merge($contactValues, ['return' => $this->getCustomFieldName('select')]));
521 $this->assertEquals('Y', $contact[$this->getCustomFieldName('select')]);
522 }
523
5699463a
SL
524 /**
525 * Test importing in the Preferred Language Field
526 *
527 * @throws \CRM_Core_Exception
528 */
529 public function testPreferredLanguageImport() {
530 $contactValues = [
531 'first_name' => 'Bill',
532 'last_name' => 'Gates',
533 'email' => 'bill.gates@microsoft.com',
534 'nick_name' => 'Billy-boy',
535 'preferred_language' => 'English (Australia)',
536 ];
537 $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, [NULL, NULL, 'Primary', NULL, NULL]);
538 }
539
6fdc0e61 540 /**
541 * Test that the import parser adds the address to the primary location.
542 *
543 * @throws \Exception
544 */
545 public function testImportDeceased() {
eba173d6 546 [$contactValues] = $this->setUpBaseContact();
6fdc0e61 547 CRM_Core_Session::singleton()->set("dateTypes", 1);
548 $contactValues['birth_date'] = '1910-12-17';
549 $contactValues['deceased_date'] = '2010-12-17';
550 $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
551 $contact = $this->callAPISuccessGetSingle('Contact', $contactValues);
552 $this->assertEquals('1910-12-17', $contact['birth_date']);
553 $this->assertEquals('2010-12-17', $contact['deceased_date']);
554 $this->assertEquals(1, $contact['is_deceased']);
9099cab3 555 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
6fdc0e61 556 }
557
6ebecfea 558 /**
559 * Test that the import parser adds the address to the primary location.
560 *
561 * @throws \Exception
562 */
563 public function testImportTwoAddressFirstPrimary() {
eba173d6 564 [$contactValues] = $this->setUpBaseContact();
6ebecfea 565 $contactValues['nick_name'] = 'Old Bill';
566 $contactValues['external_identifier'] = 'android';
567 $contactValues['street_address'] = 'Big Mansion';
568 $contactValues['phone'] = 12334;
569 $fields = array_keys($contactValues);
570 $contactValues['street_address_2'] = 'Teeny Mansion';
571 $contactValues['phone_2'] = 4444;
572 $fields[] = 'street_address';
573 $fields[] = 'phone';
9099cab3
CW
574 $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);
575 $contact = $this->callAPISuccessGetSingle('Contact', ['external_identifier' => 'android']);
576 $address = $this->callAPISuccess('Address', 'get', ['contact_id' => $contact['id'], 'sequential' => 1]);
6ebecfea 577
578 $this->assertEquals(3, $address['values'][0]['location_type_id']);
579 $this->assertEquals(0, $address['values'][0]['is_primary']);
580 $this->assertEquals('Teeny Mansion', $address['values'][0]['street_address']);
581
582 $this->assertEquals(1, $address['values'][1]['location_type_id']);
583 $this->assertEquals(1, $address['values'][1]['is_primary']);
584 $this->assertEquals('Big Mansion', $address['values'][1]['street_address']);
585
9099cab3 586 $phone = $this->callAPISuccess('Phone', 'get', ['contact_id' => $contact['id'], 'sequential' => 1]);
6ebecfea 587 $this->assertEquals(1, $phone['values'][0]['location_type_id']);
588 $this->assertEquals(1, $phone['values'][0]['is_primary']);
589 $this->assertEquals(12334, $phone['values'][0]['phone']);
590 $this->assertEquals(3, $phone['values'][1]['location_type_id']);
591 $this->assertEquals(0, $phone['values'][1]['is_primary']);
592 $this->assertEquals(4444, $phone['values'][1]['phone']);
593
9099cab3 594 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
6ebecfea 595 }
596
a7b9cf38 597 /**
598 * Test importing 2 phones of different types.
599 *
600 * @throws \CRM_Core_Exception
601 * @throws \CiviCRM_API3_Exception
602 */
603 public function testImportTwoPhonesDifferentTypes() {
604 $processor = new CRM_Import_ImportProcessor();
605 $processor->setContactType('Individual');
606 $processor->setMappingFields(
607 [
608 ['name' => 'first_name'],
609 ['name' => 'last_name'],
610 ['name' => 'email'],
611 ['name' => 'phone', 'location_type_id' => 1, 'phone_type_id' => 2],
612 ['name' => 'phone', 'location_type_id' => 1, 'phone_type_id' => 1],
613 ]
614 );
615 $importer = $processor->getImporterObject();
616 $fields = ['First Name', 'new last name', 'bob@example.com', '1234', '5678'];
617 $importer->import(CRM_Import_Parser::DUPLICATE_UPDATE, $fields);
618 $contact = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'new last name']);
619 $phones = $this->callAPISuccess('Phone', 'get', ['contact_id' => $contact['id']])['values'];
620 $this->assertCount(2, $phones);
621 }
622
6ebecfea 623 /**
624 * Test that the import parser adds the address to the primary location.
625 *
626 * @throws \Exception
627 */
628 public function testImportTwoAddressSecondPrimary() {
eba173d6 629 [$contactValues] = $this->setUpBaseContact();
6ebecfea 630 $contactValues['nick_name'] = 'Old Bill';
631 $contactValues['external_identifier'] = 'android';
632 $contactValues['street_address'] = 'Big Mansion';
633 $contactValues['phone'] = 12334;
634 $fields = array_keys($contactValues);
635 $contactValues['street_address_2'] = 'Teeny Mansion';
636 $contactValues['phone_2'] = 4444;
637 $fields[] = 'street_address';
638 $fields[] = 'phone';
9099cab3
CW
639 $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);
640 $contact = $this->callAPISuccessGetSingle('Contact', ['external_identifier' => 'android']);
641 $address = $this->callAPISuccess('Address', 'get', ['contact_id' => $contact['id'], 'sequential' => 1])['values'];
6ebecfea 642
91f1e690 643 $this->assertEquals(1, $address[1]['location_type_id']);
644 $this->assertEquals(1, $address[1]['is_primary']);
645 $this->assertEquals('Teeny Mansion', $address[1]['street_address']);
6ebecfea 646
91f1e690 647 $this->assertEquals(3, $address[0]['location_type_id']);
648 $this->assertEquals(0, $address[0]['is_primary']);
649 $this->assertEquals('Big Mansion', $address[0]['street_address']);
6ebecfea 650
2ab10f80 651 $phone = $this->callAPISuccess('Phone', 'get', ['contact_id' => $contact['id'], 'sequential' => 1, 'options' => ['sort' => 'is_primary DESC']])['values'];
91f1e690 652 $this->assertEquals(3, $phone[1]['location_type_id']);
653 $this->assertEquals(0, $phone[1]['is_primary']);
654 $this->assertEquals(12334, $phone[1]['phone']);
655 $this->assertEquals(1, $phone[0]['location_type_id']);
656 $this->assertEquals(1, $phone[0]['is_primary']);
657 $this->assertEquals(4444, $phone[0]['phone']);
6ebecfea 658
9099cab3 659 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id']]);
6ebecfea 660 }
661
662 /**
663 * Test that the import parser updates the address on the existing primary location.
664 *
665 * @throws \Exception
666 */
667 public function testImportPrimaryAddressUpdate() {
eba173d6 668 [$contactValues] = $this->setUpBaseContact(['external_identifier' => 'android']);
2ab10f80 669 $contactValues['email'] = 'melinda.gates@microsoft.com';
670 $contactValues['phone'] = '98765';
6ebecfea 671 $contactValues['external_identifier'] = 'android';
672 $contactValues['street_address'] = 'Big Mansion';
c77e8e33 673 $contactValues['city'] = 'Big City';
9099cab3
CW
674 $contactID = $this->callAPISuccessGetValue('Contact', ['external_identifier' => 'android', 'return' => 'id']);
675 $originalAddress = $this->callAPISuccess('Address', 'create', ['location_type_id' => 2, 'street_address' => 'small house', 'contact_id' => $contactID]);
676 $originalPhone = $this->callAPISuccess('phone', 'create', ['location_type_id' => 2, 'phone' => '1234', 'contact_id' => $contactID]);
677 $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 678 $phone = $this->callAPISuccessGetSingle('Phone', ['phone' => '98765']);
679 $this->assertEquals(2, $phone['location_type_id']);
680 $this->assertEquals($originalPhone['id'], $phone['id']);
681 $email = $this->callAPISuccess('Email', 'getsingle', ['contact_id' => $contactID]);
9099cab3 682 $address = $this->callAPISuccessGetSingle('Address', ['street_address' => 'Big Mansion']);
6ebecfea 683 $this->assertEquals(2, $address['location_type_id']);
684 $this->assertEquals($originalAddress['id'], $address['id']);
c77e8e33 685 $this->assertEquals('Big City', $address['city']);
10741f35 686 $this->callAPISuccessGetSingle('Contact', $contactValues);
687 }
688
13943a50
JM
689 /**
690 * Test the determination of whether a custom field is valid.
691 */
2986a716 692 public function testCustomFieldValidation(): void {
9099cab3
CW
693 $errorMessage = [];
694 $customGroup = $this->customGroupCreate([
3b2c326e 695 'extends' => 'Contact',
f1230f06 696 'title' => 'ABC',
9099cab3 697 ]);
1dd8e8ce 698 $customField = $this->customFieldOptionValueCreate($customGroup, 'fieldABC', ['html_type' => 'Select', 'serialize' => 1]);
9099cab3 699 $params = [
412585fb 700 'custom_' . $customField['id'] => 'Label1|Label2',
9099cab3 701 ];
412585fb 702 CRM_Contact_Import_Parser_Contact::isErrorInCustomData($params, $errorMessage);
9099cab3 703 $this->assertEquals([], $errorMessage);
412585fb 704 }
705
6e975197
JM
706 /**
707 * Test that setting duplicate action to fill doesn't blow away data
708 * that exists, but does fill in where it's empty.
709 *
710 * @throw \Exception
711 */
712 public function testImportFill() {
713 // Create a custom field group for testing.
a0c6165f 714 $this->createCustomGroup([
715 'title' => 'importFillGroup',
716 'extends' => 'Individual',
717 'is_active' => TRUE,
718 ]);
719 $customGroupID = $this->ids['CustomGroup']['importFillGroup'];
6e975197
JM
720
721 // Add two custom fields.
9099cab3 722 $api_params = [
a0c6165f 723 'custom_group_id' => $customGroupID,
6e975197
JM
724 'label' => 'importFillField1',
725 'html_type' => 'Select',
726 'data_type' => 'String',
9099cab3 727 'option_values' => [
6e975197
JM
728 'foo' => 'Foo',
729 'bar' => 'Bar',
9099cab3
CW
730 ],
731 ];
6e975197
JM
732 $result = $this->callAPISuccess('custom_field', 'create', $api_params);
733 $customField1 = $result['id'];
734
9099cab3 735 $api_params = [
a0c6165f 736 'custom_group_id' => $customGroupID,
6e975197
JM
737 'label' => 'importFillField2',
738 'html_type' => 'Select',
739 'data_type' => 'String',
9099cab3 740 'option_values' => [
6e975197
JM
741 'baz' => 'Baz',
742 'boo' => 'Boo',
9099cab3
CW
743 ],
744 ];
6e975197
JM
745 $result = $this->callAPISuccess('custom_field', 'create', $api_params);
746 $customField2 = $result['id'];
747
748 // Now set up values.
749 $original_gender = 'Male';
750 $original_custom1 = 'foo';
6e975197
JM
751 $original_email = 'test-import-fill@example.org';
752
753 $import_gender = 'Female';
754 $import_custom1 = 'bar';
755 $import_job_title = 'Chief data importer';
756 $import_custom2 = 'baz';
757
758 // Create contact with both one known core field and one custom
759 // field filled in.
9099cab3 760 $api_params = [
6e975197
JM
761 'contact_type' => 'Individual',
762 'email' => $original_email,
763 'gender' => $original_gender,
f3cfbe94 764 'custom_' . $customField1 => $original_custom1,
9099cab3 765 ];
6e975197
JM
766 $result = $this->callAPISuccess('contact', 'create', $api_params);
767 $contact_id = $result['id'];
768
769 // Run an import.
9099cab3 770 $import = [
6e975197
JM
771 'email' => $original_email,
772 'gender_id' => $import_gender,
773 'custom_' . $customField1 => $import_custom1,
774 'job_title' => $import_job_title,
f3cfbe94 775 'custom_' . $customField2 => $import_custom2,
9099cab3 776 ];
6e975197
JM
777
778 $this->runImport($import, CRM_Import_Parser::DUPLICATE_FILL, CRM_Import_Parser::VALID);
779
9099cab3 780 $expected = [
6e975197
JM
781 'gender' => $original_gender,
782 'custom_' . $customField1 => $original_custom1,
783 'job_title' => $import_job_title,
f3cfbe94 784 'custom_' . $customField2 => $import_custom2,
9099cab3 785 ];
6e975197 786
9099cab3 787 $params = [
6e975197 788 'id' => $contact_id,
9099cab3 789 'return' => [
6e975197
JM
790 'gender',
791 'custom_' . $customField1,
792 'job_title',
f3cfbe94 793 'custom_' . $customField2,
9099cab3
CW
794 ],
795 ];
6e975197
JM
796 $result = civicrm_api3('Contact', 'get', $params);
797 $values = array_pop($result['values']);
f3cfbe94 798 foreach ($expected as $field => $expected_value) {
6e975197 799 if (!isset($values[$field])) {
f3cfbe94 800 $given_value = NULL;
6e975197
JM
801 }
802 else {
803 $given_value = $values[$field];
804 }
805 // We expect:
806 // gender: Male
807 // job_title: Chief Data Importer
808 // importFillField1: foo
809 // importFillField2: baz
810 $this->assertEquals($expected_value, $given_value, "$field properly handled during Fill import");
811 }
812 }
813
4352bd72 814 /**
815 * CRM-19888 default country should be used if ambigous.
697f7b54 816 *
817 * @throws \CRM_Core_Exception
4352bd72 818 */
697f7b54 819 public function testImportAmbiguousStateCountry(): void {
1c8738dd 820 $this->callAPISuccess('Setting', 'create', ['defaultContactCountry' => 1228]);
4352bd72 821 $countries = CRM_Core_PseudoConstant::country(FALSE, FALSE);
9099cab3
CW
822 $this->callAPISuccess('Setting', 'create', ['countryLimit' => [array_search('United States', $countries), array_search('Guyana', $countries), array_search('Netherlands', $countries)]]);
823 $this->callAPISuccess('Setting', 'create', ['provinceLimit' => [array_search('United States', $countries), array_search('Guyana', $countries), array_search('Netherlands', $countries)]]);
824 $mapper = [0 => NULL, 1 => NULL, 2 => 'Primary', 3 => NULL];
697f7b54 825 [$contactValues] = $this->setUpBaseContact();
4352bd72 826 $fields = array_keys($contactValues);
9099cab3 827 $addressValues = [
4352bd72 828 'street_address' => 'PO Box 2716',
829 'city' => 'Midway',
830 'state_province' => 'UT',
831 'postal_code' => 84049,
832 'country' => 'United States',
9099cab3
CW
833 ];
834 $locationTypes = $this->callAPISuccess('Address', 'getoptions', ['field' => 'location_type_id']);
4352bd72 835 $locationTypes = $locationTypes['values'];
836 foreach ($addressValues as $field => $value) {
837 $contactValues['home_' . $field] = $value;
838 $mapper[] = array_search('Home', $locationTypes);
839 $contactValues['work_' . $field] = $value;
840 $mapper[] = array_search('Work', $locationTypes);
841 $fields[] = $field;
842 $fields[] = $field;
843 }
844 $contactValues['work_country'] = '';
845
846 $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, $mapper, $fields);
9099cab3 847 $addresses = $this->callAPISuccess('Address', 'get', ['contact_id' => ['>' => 2], 'sequential' => 1]);
4352bd72 848 $this->assertEquals(2, $addresses['count']);
849 $this->assertEquals(array_search('United States', $countries), $addresses['values'][0]['country_id']);
850 $this->assertEquals(array_search('United States', $countries), $addresses['values'][1]['country_id']);
851 }
852
e87ff4ce 853 /**
854 * Run the import parser.
855 *
856 * @param array $originalValues
857 *
858 * @param int $onDuplicateAction
859 * @param int $expectedResult
10741f35 860 * @param array|null $mapperLocType
91f1e690 861 * Array of location types that map to the input arrays.
6ebecfea 862 * @param array|null $fields
863 * Array of field names. Will be calculated from $originalValues if not passed in, but
864 * that method does not cope with duplicates.
b4e15ec2
JG
865 * @param int|null $ruleGroupId
866 * To test against a specific dedupe rule group, pass its ID as this argument.
eba173d6 867 *
868 * @throws \CRM_Core_Exception
869 * @throws \CiviCRM_API3_Exception
e87ff4ce 870 */
eba173d6 871 protected function runImport(array $originalValues, $onDuplicateAction, $expectedResult, $mapperLocType = [], $fields = NULL, int $ruleGroupId = NULL): void {
6ebecfea 872 if (!$fields) {
873 $fields = array_keys($originalValues);
874 }
e87ff4ce 875 $values = array_values($originalValues);
10741f35 876 $parser = new CRM_Contact_Import_Parser_Contact($fields, $mapperLocType);
e87ff4ce 877 $parser->_contactType = 'Individual';
b4e15ec2 878 $parser->_dedupeRuleGroupID = $ruleGroupId;
e87ff4ce 879 $parser->_onDuplicate = $onDuplicateAction;
880 $parser->init();
65070890 881 $this->assertEquals($expectedResult, $parser->import($onDuplicateAction, $values), 'Return code from parser import was not as expected');
e87ff4ce 882 }
ffe87781 883
755a3281
AP
884 /**
885 * @param array $fields Array of fields to be imported
886 * @param array $allfields Array of all fields which can be part of import
887 */
888 private function mapRelationshipFields(&$fields, $allfields) {
889 foreach ($allfields as $key => $fieldtocheck) {
890 $elementIndex = array_search($fieldtocheck->_title, $fields);
891 if ($elementIndex !== FALSE) {
892 $fields[$elementIndex] = $key;
893 }
894 }
895 }
896
8fd37b20 897 /**
898 * Set up the underlying contact.
899 *
900 * @param array $params
901 * Optional extra parameters to set.
902 *
903 * @return array
44134d5c 904 * @throws \CRM_Core_Exception
8fd37b20 905 */
9099cab3
CW
906 protected function setUpBaseContact($params = []) {
907 $originalValues = array_merge([
8fd37b20 908 'first_name' => 'Bill',
909 'last_name' => 'Gates',
910 'email' => 'bill.gates@microsoft.com',
911 'nick_name' => 'Billy-boy',
9099cab3 912 ], $params);
8fd37b20 913 $this->runImport($originalValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID);
914 $result = $this->callAPISuccessGetSingle('Contact', $originalValues);
9099cab3 915 return [$originalValues, $result];
8fd37b20 916 }
917
e87ff4ce 918}