Merge pull request #16469 from civicrm/5.22
[civicrm-core.git] / tests / phpunit / api / v3 / AddressTest.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work 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 +--------------------------------------------------------------------+
10 */
11
12 /**
13 * Test APIv3 civicrm_activity_* functions
14 *
15 * @package CiviCRM_APIv3
16 * @subpackage API_Contact
17 */
18
19 /**
20 * Class api_v3_AddressTest
21 * @group headless
22 */
23 class api_v3_AddressTest extends CiviUnitTestCase {
24 protected $_contactID;
25 protected $_locationType;
26 protected $_params;
27
28 protected $_entity;
29
30 public function setUp() {
31 $this->_entity = 'Address';
32 parent::setUp();
33
34 $this->_contactID = $this->organizationCreate();
35 $this->_locationType = $this->locationTypeCreate();
36 CRM_Core_PseudoConstant::flush();
37
38 $this->_params = [
39 'contact_id' => $this->_contactID,
40 'location_type_id' => $this->_locationType->id,
41 'street_name' => 'Ambachtstraat',
42 'street_number' => '23',
43 'street_address' => 'Ambachtstraat 23',
44 'postal_code' => '6971 BN',
45 'country_id' => '1152',
46 'city' => 'Brummen',
47 'is_primary' => 1,
48 ];
49 }
50
51 public function tearDown() {
52 $this->locationTypeDelete($this->_locationType->id);
53 $this->contactDelete($this->_contactID);
54 $this->quickCleanup(['civicrm_address', 'civicrm_relationship']);
55 parent::tearDown();
56 }
57
58 /**
59 * @param int $version
60 * @dataProvider versionThreeAndFour
61 */
62 public function testCreateAddress($version) {
63 $this->_apiversion = $version;
64 $result = $this->callAPIAndDocument('address', 'create', $this->_params, __FUNCTION__, __FILE__);
65 $this->assertEquals(1, $result['count']);
66 $this->assertNotNull($result['values'][$result['id']]['id']);
67 $this->getAndCheck($this->_params, $result['id'], 'address');
68 }
69
70 /**
71 * @param int $version
72 * @dataProvider versionThreeAndFour
73 */
74 public function testCreateAddressParsing($version) {
75 $this->_apiversion = $version;
76 $params = [
77 'street_parsing' => 1,
78 'street_address' => '54A Excelsior Ave. Apt 1C',
79 'location_type_id' => $this->_locationType->id,
80 'contact_id' => $this->_contactID,
81 ];
82 $subfile = "AddressParse";
83 $description = "Demonstrates Use of address parsing param.";
84 $result = $this->callAPIAndDocument('address', 'create', $params, __FUNCTION__, __FILE__, $description, $subfile);
85 $this->assertEquals(54, $result['values'][$result['id']]['street_number']);
86 $this->assertEquals('A', $result['values'][$result['id']]['street_number_suffix']);
87 $this->assertEquals('Excelsior Ave.', $result['values'][$result['id']]['street_name']);
88 $this->assertEquals('Apt 1C', $result['values'][$result['id']]['street_unit']);
89 $this->callAPISuccess('address', 'delete', ['id' => $result['id']]);
90
91 }
92
93 /**
94 * Is_primary should be set as a default.
95 * @param int $version
96 * @dataProvider versionThreeAndFour
97 */
98 public function testCreateAddressTestDefaults($version) {
99 $this->_apiversion = $version;
100 $params = $this->_params;
101 unset($params['is_primary']);
102 $result = $this->callAPISuccess('address', 'create', $params);
103 $this->assertEquals(1, $result['count']);
104 $this->assertEquals(1, $result['values'][$result['id']]['is_primary']);
105 $this->getAndCheck($this->_params, $result['id'], 'address');
106 }
107
108 /**
109 * If no location is specified when creating a new address, it should default to
110 * the LocationType default
111 *
112 * @param int $version
113 * @dataProvider versionThreeAndFour
114 */
115 public function testCreateAddressDefaultLocation($version) {
116 $this->_apiversion = $version;
117 $params = $this->_params;
118 unset($params['location_type_id']);
119 $result = $this->callAPIAndDocument($this->_entity, 'create', $params, __FUNCTION__, __FILE__);
120 $this->assertEquals(CRM_Core_BAO_LocationType::getDefault()->id, $result['values'][$result['id']]['location_type_id']);
121 $this->callAPISuccess($this->_entity, 'delete', ['id' => $result['id']]);
122 }
123
124 /**
125 * FIXME: Api4
126 */
127 public function testCreateAddressTooLongSuffix() {
128 $params = $this->_params;
129 $params['street_number_suffix'] = 'really long string';
130 $this->callAPIFailure('address', 'create', $params);
131 }
132
133 /**
134 * Create an address with a master ID and ensure that a relationship is created.
135 * @param int $version
136 * @dataProvider versionThreeAndFour
137 */
138 public function testCreateAddressWithMasterRelationshipHousehold($version) {
139 $this->_apiversion = $version;
140 $householdID = $this->householdCreate();
141 $address = $this->callAPISuccess('address', 'create', array_merge($this->_params, $this->_params, ['contact_id' => $householdID]));
142 $individualID = $this->individualCreate();
143 $individualParams = [
144 'contact_id' => $individualID,
145 'master_id' => $address['id'],
146 ];
147 $this->callAPISuccess('address', 'create', array_merge($this->_params, $individualParams));
148 $this->callAPISuccess('relationship', 'getcount', [
149 'contact_id_a' => $individualID,
150 'contact_id_b' => $this->_contactID,
151 ]);
152 }
153
154 /**
155 * Create an address with a master ID and ensure that a relationship is created.
156 * @param int $version
157 * @dataProvider versionThreeAndFour
158 */
159 public function testCreateAddressWithMasterRelationshipOrganization($version) {
160 $this->_apiversion = $version;
161 $address = $this->callAPISuccess('address', 'create', $this->_params);
162 $individualID = $this->individualCreate();
163 $individualParams = [
164 'contact_id' => $individualID,
165 'master_id' => $address['id'],
166 ];
167 $this->callAPISuccess('address', 'create', array_merge($this->_params, $individualParams));
168 $this->callAPISuccess('relationship', 'getcount', [
169 'contact_id_a' => $individualID,
170 'contact_id_b' => $this->_contactID,
171 ], 1);
172 }
173
174 /**
175 * Create an address with a master ID and relationship creation disabled.
176 * @param int $version
177 * @dataProvider versionThreeAndFour
178 */
179 public function testCreateAddressWithoutMasterRelationshipOrganization($version) {
180 $this->_apiversion = $version;
181 $address = $this->callAPISuccess('address', 'create', $this->_params);
182 $individualID = $this->individualCreate();
183 $individualParams = [
184 'contact_id' => $individualID,
185 'master_id' => $address['id'],
186 'add_relationship' => 0,
187 ];
188 $this->callAPISuccess('address', 'create', array_merge($this->_params, $individualParams));
189 $this->callAPISuccess('relationship', 'getcount', [
190 'contact_id_a' => $individualID,
191 'contact_id_b' => $this->_contactID,
192 ], 0);
193 }
194
195 /**
196 * Create an address with a master ID and ensure that a relationship is created.
197 * @param int $version
198 * @dataProvider versionThreeAndFour
199 */
200 public function testCreateAddressWithMasterRelationshipChangingOrganization($version) {
201 $this->_apiversion = $version;
202 $address = $this->callAPISuccess('address', 'create', $this->_params);
203 $organisation2ID = $this->organizationCreate();
204 $address2 = $this->callAPISuccess('address', 'create', array_merge($this->_params, ['contact_id' => $organisation2ID]));
205 $individualID = $this->individualCreate();
206 $individualParams = array_merge($this->_params, [
207 'contact_id' => $individualID,
208 'master_id' => $address['id'],
209 ]);
210 $individualAddress = $this->callAPISuccess('address', 'create', $individualParams);
211 $individualParams['master_id'] = $address2['id'];
212 $individualParams['id'] = $individualAddress['id'];
213 $this->callAPISuccess('address', 'create', $individualParams);
214 $this->callAPISuccessGetCount('relationship', ['contact_id_a' => $individualID], 2);
215 $this->markTestIncomplete('Remainder of test checks that employer relationship is disabled when new one is created but turns out to be not happening - by design?');
216 $this->callAPISuccessGetCount('relationship', ['contact_id_a' => $individualID, 'is_active' => FALSE], 1);
217 $this->callAPISuccessGetCount('relationship', [
218 'contact_id_a' => $individualID,
219 'is_active' => TRUE,
220 'contact_id_b' => $organisation2ID,
221 ], 1);
222
223 }
224
225 /**
226 * Is_primary should be set as a default.
227 *
228 * ie. create the address, unset the params & recreate.
229 * is_primary should be 0 before & after the update. ie - having no other address
230 * is_primary is invalid.
231 * @param int $version
232 * @dataProvider versionThreeAndFour
233 */
234 public function testCreateAddressTestDefaultWithID($version) {
235 $this->_apiversion = $version;
236 $params = $this->_params;
237 $params['is_primary'] = 0;
238 $result = $this->callAPISuccess('address', 'create', $params);
239 unset($params['is_primary']);
240 $params['id'] = $result['id'];
241 $this->callAPISuccess('address', 'create', $params);
242 $result = $this->callAPISuccess('address', 'get', ['contact_id' => $params['contact_id']]);
243 $this->assertEquals(1, $result['count']);
244 $this->assertEquals(1, $result['values'][$result['id']]['is_primary']);
245 $this->getAndCheck($params, $result['id'], 'address', __FUNCTION__);
246 }
247
248 /**
249 * test address deletion.
250 * @param int $version
251 * @dataProvider versionThreeAndFour
252 */
253 public function testDeleteAddress($version) {
254 $this->_apiversion = $version;
255 //check there are no address to start with
256 $get = $this->callAPISuccess('address', 'get', [
257 'location_type_id' => $this->_locationType->id,
258 ]);
259 $this->assertEquals(0, $get['count'], 'Contact already exists ');
260
261 //create one
262 $create = $this->callAPISuccess('address', 'create', $this->_params);
263
264 $result = $this->callAPIAndDocument('address', 'delete', ['id' => $create['id']], __FUNCTION__, __FILE__);
265 $this->assertEquals(1, $result['count']);
266 $get = $this->callAPISuccess('address', 'get', [
267 'location_type_id' => $this->_locationType->id,
268 ]);
269 $this->assertEquals(0, $get['count'], 'Contact not successfully deleted In line ' . __LINE__);
270 }
271
272 /**
273 * Test civicrm_address_get - success expected.
274 * @param int $version
275 * @dataProvider versionThreeAndFour
276 */
277 public function testGetAddress($version) {
278 $this->_apiversion = $version;
279 $address = $this->callAPISuccess('address', 'create', $this->_params);
280
281 $params = [
282 'contact_id' => $this->_contactID,
283 'street_name' => $address['values'][$address['id']]['street_name'],
284 ];
285 $result = $this->callAPIAndDocument('Address', 'Get', $params, __FUNCTION__, __FILE__);
286 $this->callAPISuccess('Address', 'delete', ['id' => $result['id']]);
287 $this->assertEquals($address['values'][$address['id']]['location_type_id'], $result['values'][$address['id']]['location_type_id']);
288 $this->assertEquals($address['values'][$address['id']]['is_primary'], $result['values'][$address['id']]['is_primary']);
289 $this->assertEquals($address['values'][$address['id']]['street_address'], $result['values'][$address['id']]['street_address']);
290 }
291
292 /**
293 * Test civicrm_address_get - success expected.
294 * @param int $version
295 * @dataProvider versionThreeAndFour
296 */
297 public function testGetSingleAddress($version) {
298 $this->_apiversion = $version;
299 $this->callAPISuccess('address', 'create', $this->_params);
300 $params = [
301 'contact_id' => $this->_contactID,
302 ];
303 $address = $this->callAPISuccess('Address', 'getsingle', ($params));
304 $this->assertEquals($address['location_type_id'], $this->_params['location_type_id']);
305 $this->callAPISuccess('address', 'delete', ['id' => $address['id']]);
306 }
307
308 /**
309 * Test civicrm_address_get with sort option- success expected.
310 * @param int $version
311 * @dataProvider versionThreeAndFour
312 */
313 public function testGetAddressSort($version) {
314 $this->_apiversion = $version;
315 $create = $this->callAPISuccess('address', 'create', $this->_params);
316 $this->callAPISuccess('address', 'create', array_merge($this->_params, ['street_address' => 'yzy']));
317 $subfile = "AddressSort";
318 $description = "Demonstrates Use of sort filter.";
319 $params = [
320 'options' => [
321 'sort' => 'street_address DESC',
322 'limit' => 2,
323 ],
324 'sequential' => 1,
325 ];
326 $result = $this->callAPIAndDocument('Address', 'Get', $params, __FUNCTION__, __FILE__, $description, $subfile);
327 $this->assertEquals(2, $result['count']);
328 $this->assertEquals('Ambachtstraat 23', $result['values'][1]['street_address']);
329 $this->callAPISuccess('address', 'delete', ['id' => $create['id']]);
330 }
331
332 /**
333 * Test civicrm_address_get with sort option- success expected.
334 * @param int $version
335 * @dataProvider versionThreeAndFour
336 */
337 public function testGetAddressLikeSuccess($version) {
338 $this->_apiversion = $version;
339 $this->callAPISuccess('address', 'create', $this->_params);
340 $subfile = "AddressLike";
341 $description = "Demonstrates Use of Like.";
342 $params = [
343 'street_address' => ['LIKE' => '%mb%'],
344 'sequential' => 1,
345 ];
346 $result = $this->callAPIAndDocument('Address', 'Get', $params, __FUNCTION__, __FILE__, $description, $subfile);
347 $this->assertEquals(1, $result['count']);
348 $this->assertEquals('Ambachtstraat 23', $result['values'][0]['street_address']);
349 $this->callAPISuccess('address', 'delete', ['id' => $result['id']]);
350 }
351
352 /**
353 * Test civicrm_address_get with sort option- success expected.
354 * @param int $version
355 * @dataProvider versionThreeAndFour
356 */
357 public function testGetAddressLikeFail($version) {
358 $this->_apiversion = $version;
359 $create = $this->callAPISuccess('address', 'create', $this->_params);
360 $params = [
361 'street_address' => ['LIKE' => "'%xy%'"],
362 'sequential' => 1,
363 ];
364 $result = $this->callAPISuccess('Address', 'Get', ($params));
365 $this->assertEquals(0, $result['count']);
366 $this->callAPISuccess('address', 'delete', ['id' => $create['id']]);
367 }
368
369 /**
370 * FIXME: Api4 custom address fields broken?
371 */
372 public function testGetWithCustom() {
373 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
374
375 $params = $this->_params;
376 $params['custom_' . $ids['custom_field_id']] = "custom string";
377
378 $result = $this->callAPISuccess($this->_entity, 'create', $params);
379
380 $getParams = ['id' => $result['id'], 'return' => ['custom']];
381 $check = $this->callAPISuccess($this->_entity, 'get', $getParams);
382
383 $this->assertEquals("custom string", $check['values'][$check['id']]['custom_' . $ids['custom_field_id']], ' in line ' . __LINE__);
384
385 $this->customFieldDelete($ids['custom_field_id']);
386 $this->customGroupDelete($ids['custom_group_id']);
387 $this->callAPISuccess('address', 'delete', ['id' => $result['id']]);
388 }
389
390 /**
391 * @param int $version
392 * @dataProvider versionThreeAndFour
393 */
394 public function testCreateAddressPrimaryHandlingChangeToPrimary($version) {
395 $this->_apiversion = $version;
396 $params = $this->_params;
397 unset($params['is_primary']);
398 $address1 = $this->callAPISuccess('address', 'create', $params);
399 $this->assertApiSuccess($address1);
400 //now we check & make sure it has been set to primary
401 $check = $this->callAPISuccess('address', 'getcount', [
402 'is_primary' => 1,
403 'id' => $address1['id'],
404 ]);
405 $this->assertEquals(1, $check);
406 $this->callAPISuccess('address', 'delete', ['id' => $address1['id']]);
407 }
408
409 /**
410 * @param int $version
411 * @dataProvider versionThreeAndFour
412 */
413 public function testCreateAddressPrimaryHandlingChangeExisting($version) {
414 $this->_apiversion = $version;
415 $address1 = $this->callAPISuccess('address', 'create', $this->_params);
416 $this->callAPISuccess('address', 'create', $this->_params);
417 $check = $this->callAPISuccess('address', 'getcount', [
418 'is_primary' => 1,
419 'contact_id' => $this->_contactID,
420 ]);
421 $this->assertEquals(1, $check);
422 $this->callAPISuccess('address', 'delete', ['id' => $address1['id']]);
423 }
424
425 /**
426 * Test Creating address of same type alreay ind the database
427 * This is legacy API v3 behaviour and not correct behaviour
428 * however we are too far down the path wiwth v3 to fix this
429 * @link https://chat.civicrm.org/civicrm/pl/zcq3jkg69jdt5g4aqze6bbe9pc
430 * FIXME: Api4
431 */
432 public function testCreateDuplicateLocationTypes() {
433 $address1 = $this->callAPISuccess('address', 'create', $this->_params);
434 $address2 = $this->callAPISuccess('address', 'create', [
435 'location_type_id' => $this->_locationType->id,
436 'street_address' => '1600 Pensilvania Avenue',
437 'city' => 'Washington DC',
438 'is_primary' => 0,
439 'is_billing' => 0,
440 'contact_id' => $this->_contactID,
441 ]);
442 $check = $this->callAPISuccess('address', 'getcount', [
443 'contact_id' => $this->_contactID,
444 'location_type_id' => $this->_locationType->id,
445 ]);
446 $this->assertEquals(2, $check);
447 $this->callAPISuccess('address', 'delete', ['id' => $address1['id']]);
448 $this->callAPISuccess('address', 'delete', ['id' => $address2['id']]);
449 }
450
451 public function testGetWithJoin() {
452 $cid = $this->individualCreate([
453 'api.Address.create' => [
454 'street_address' => __FUNCTION__,
455 'location_type_id' => $this->_locationType->id,
456 ],
457 ]);
458 $result = $this->callAPISuccess('address', 'getsingle', [
459 'check_permissions' => TRUE,
460 'contact_id' => $cid,
461 'street_address' => __FUNCTION__,
462 'return' => 'contact_id.contact_type',
463 ]);
464 $this->assertEquals('Individual', $result['contact_id.contact_type']);
465 }
466
467 /**
468 * Test Address create with a state name that at least two countries have, e.g. Maryland, United States vs. Maryland, Liberia
469 *
470 * @see https://lab.civicrm.org/dev/core/issues/725
471 */
472 public function testCreateAddressStateProvinceIDCorrectForCountry() {
473 $params = $this->_params;
474 $params['sequential'] = 1;
475 // United States country id
476 $params['country_id'] = '1228';
477 $params['state_province_id'] = 'Maryland';
478 $params['city'] = 'Baltimore';
479 $params['street_address'] = '600 N Charles St.';
480 $params['postal_code'] = '21201';
481 unset($params['street_name']);
482 unset($params['street_number']);
483 $address1 = $this->callAPISuccess('address', 'create', $params);
484 // should find state_province_id of 1019, Maryland, United States ... NOT 3497, Maryland, Liberia
485 $this->assertEquals('1019', $address1['values'][0]['state_province_id']);
486
487 // Now try it in Liberia
488 $params = $this->_params;
489 $params['sequential'] = 1;
490 // Liberia country id
491 $params['country_id'] = '1122';
492 $params['state_province_id'] = 'Maryland';
493 $address2 = $this->callAPISuccess('address', 'create', $params);
494 $this->assertEquals('3497', $address2['values'][0]['state_province_id']);
495 }
496
497 public function getSymbolicCountryStateExamples() {
498 return [
499 // [mixed $inputCountry, mixed $inputState, int $expectCountry, int $expectState]
500 [1228, 1004, 1228, 1004],
501 //['US', 'CA', 1228, 1004],
502 //['US', 'TX', 1228, 1042],
503 ['US', 'California', 1228, 1004],
504 [1228, 'Texas', 1228, 1042],
505 // Don't think these have been supported?
506 // ['United States', 1004, 1228, 1004] ,
507 // ['United States', 'TX', 1228, 1042],
508 ];
509 }
510
511 /**
512 * @param mixed $inputCountry
513 * Ex: 1228 or 'US'
514 * @param mixed $inputState
515 * Ex: 1004 or 'CA'
516 * @param int $expectCountry
517 * @param int $expectState
518 * @dataProvider getSymbolicCountryStateExamples
519 */
520 public function testCreateAddressSymbolicCountryAndState($inputCountry, $inputState, $expectCountry, $expectState) {
521 $cid = $this->individualCreate();
522 $r = $this->callAPISuccess('Address', 'create', [
523 'contact_id' => $cid,
524 'location_type_id' => 1,
525 'street_address' => '123 Some St',
526 'city' => 'Hereville',
527 //'US',
528 'country_id' => $inputCountry,
529 // 'California',
530 'state_province_id' => $inputState,
531 'postal_code' => '94100',
532 ]);
533 $created = CRM_Utils_Array::first($r['values']);
534 $this->assertEquals($expectCountry, $created['country_id']);
535 $this->assertEquals($expectState, $created['state_province_id']);
536 }
537
538 /**
539 * @param int $version
540 * @dataProvider versionThreeAndFour
541 */
542 public function testBuildStateProvinceOptionsWithDodgyProvinceLimit($version) {
543 $this->_apiversion = $version;
544 $provinceLimit = [1228, "abcd;ef"];
545 $this->callAPISuccess('setting', 'create', [
546 'provinceLimit' => $provinceLimit,
547 ]);
548 $result = $this->callAPIFailure('address', 'getoptions', ['field' => 'state_province_id']);
549 // confirm that we hit our error not a SQLI.
550 $this->assertEquals('Province limit or default country setting is incorrect', $result['error_message']);
551 $this->callAPISuccess('setting', 'create', [
552 'provinceLimit' => [1228],
553 ]);
554 // Now confirm with a correct province setting it works fine
555 $this->callAPISuccess('address', 'getoptions', ['field' => 'state_province_id']);
556 }
557
558 /**
559 * @param int $version
560 * @dataProvider versionThreeAndFour
561 */
562 public function testBuildCountryWithDodgyCountryLimitSetting($version) {
563 $this->_apiversion = $version;
564 $countryLimit = [1228, "abcd;ef"];
565 $this->callAPISuccess('setting', 'create', [
566 'countryLimit' => $countryLimit,
567 ]);
568 $result = $this->callAPIFailure('address', 'getoptions', ['field' => 'country_id']);
569 // confirm that we hit our error not a SQLI.
570 $this->assertEquals('Available Country setting is incorrect', $result['error_message']);
571 $this->callAPISuccess('setting', 'create', [
572 'countryLimit' => [1228],
573 ]);
574 // Now confirm with a correct province setting it works fine
575 $this->callAPISuccess('address', 'getoptions', ['field' => 'country_id']);
576 }
577
578 public function testBuildCountyWithDodgeStateProvinceFiltering() {
579 $result = $this->callAPIFailure('Address', 'getoptions', [
580 'field' => 'county_id',
581 'state_province_id' => "abcd;ef",
582 ]);
583 $this->assertEquals('Can only accept Integers for state_province_id filtering', $result['error_message']);
584 $goodResult = $this->callAPISuccess('Address', 'getoptions', [
585 'field' => 'county_id',
586 'state_province_id' => 1004,
587 ]);
588 $this->assertEquals('San Francisco', $goodResult['values'][4]);
589 }
590
591 public function testGetOptionsAbbr() {
592 $result = $this->callAPISuccess('Address', 'getoptions', [
593 'field' => 'country_id',
594 'context' => "abbreviate",
595 ]);
596 $this->assertContains('US', $result['values']);
597 $this->assertNotContains('United States', $result['values']);
598 $result = $this->callAPISuccess('Address', 'getoptions', [
599 'field' => 'state_province_id',
600 'context' => "abbreviate",
601 ]);
602 $this->assertContains('AL', $result['values']);
603 $this->assertNotContains('Alabama', $result['values']);
604 }
605
606 }