Merge pull request #19892 from colemanw/searchKitCreatedModified
[civicrm-core.git] / tests / phpunit / CRM / Contact / BAO / RelationshipTest.php
CommitLineData
02e028a0
AS
1<?php
2/*
7d61e75f
TO
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 +--------------------------------------------------------------------+
02e028a0
AS
10 */
11
12/**
13 * Test class for CRM_Contact_BAO_Relationship
14 *
15 * @package CiviCRM
16 * @group headless
17 */
18class CRM_Contact_BAO_RelationshipTest extends CiviUnitTestCase {
19
20 /**
21 * Sets up the fixture, for example, opens a network connection.
22 *
23 * This method is called before a test is executed.
24 */
25 protected function setUp() {
26 parent::setUp();
27 }
28
29 /**
30 * Tears down the fixture, for example, closes a network connection.
31 *
32 * This method is called after a test is executed.
310c2031 33 *
34 * @throws \CRM_Core_Exception
02e028a0
AS
35 */
36 protected function tearDown() {
89e45979
MD
37 $this->quickCleanup([
38 'civicrm_relationship_type',
39 'civicrm_relationship',
39b959db 40 'civicrm_contact',
89e45979
MD
41 ]);
42
02e028a0
AS
43 parent::tearDown();
44 }
45
310c2031 46 /**
47 * Test Relationship Type Options Will Return Specified Type
48 *
49 * @throws \CRM_Core_Exception
50 */
89e45979
MD
51 public function testRelationshipTypeOptionsWillReturnSpecifiedType() {
52 $orgToOrgType = 'A_B_relationship';
53 $orgToOrgReverseType = 'B_A_relationship';
310c2031 54 $this->callAPISuccess('RelationshipType', 'create', [
89e45979
MD
55 'name_a_b' => $orgToOrgType,
56 'name_b_a' => $orgToOrgReverseType,
57 'contact_type_a' => 'Organization',
58 'contact_type_b' => 'Organization',
59 ]);
60
1ac9bb56
CW
61 $result = civicrm_api3('Relationship', 'getoptions', [
62 'field' => 'relationship_type_id',
63 'is_form' => TRUE,
64 'contact_type' => 'Organization',
65 ]);
66 $this->assertContains($orgToOrgType, $result['values']);
67 $this->assertContains($orgToOrgReverseType, $result['values']);
68
69 $result = civicrm_api3('Relationship', 'getoptions', [
70 'field' => 'relationship_type_id',
71 'is_form' => TRUE,
72 'contact_type' => 'Individual',
73 ]);
74
75 $this->assertNotContains($orgToOrgType, $result['values']);
76 $this->assertNotContains($orgToOrgReverseType, $result['values']);
89e45979
MD
77 }
78
79 public function testContactIdAndRelationshipIdWillBeUsedInFilter() {
80 $individual = civicrm_api3('Contact', 'create', [
81 'display_name' => 'Individual A',
82 'contact_type' => 'Individual',
83 ]);
84 $organization = civicrm_api3('Contact', 'create', [
85 'organization_name' => 'Organization B',
86 'contact_type' => 'Organization',
87 ]);
88
89 $personToOrgType = 'A_B_relationship';
90 $orgToPersonType = 'B_A_relationship';
91
92 $orgToPersonTypeId = civicrm_api3('RelationshipType', 'create', [
93 'name_a_b' => $personToOrgType,
94 'name_b_a' => $orgToPersonType,
95 'contact_type_a' => 'Individual',
96 'contact_type_b' => 'Organization',
97 ])['id'];
98
99 $personToPersonType = 'A_B_alt_relationship';
100 $personToPersonReverseType = 'B_A_alt_relationship';
101
102 civicrm_api3('RelationshipType', 'create', [
103 'name_a_b' => $personToPersonType,
104 'name_b_a' => $personToPersonReverseType,
105 'contact_type_a' => 'Individual',
106 'contact_type_b' => 'Individual',
107 ]);
108
109 // create a relationship individual => organization
110 $relationship = civicrm_api3('Relationship', 'create', [
111 'contact_id_a' => $individual['id'],
112 'contact_id_b' => $organization['id'],
113 'relationship_type_id' => $orgToPersonTypeId,
114 ]);
115
1ac9bb56
CW
116 $options = civicrm_api3('Relationship', 'getoptions', [
117 'field' => 'relationship_type_id',
118 'is_form' => TRUE,
89e45979 119 'relationship_id' => (string) $relationship['id'],
39b959db 120 'contact_id' => $individual['id'],
89e45979
MD
121 ]);
122
123 // for this relationship only individual=>organization is possible
1ac9bb56
CW
124 $this->assertContains($personToOrgType, $options['values']);
125 $this->assertNotContains($orgToPersonType, $options['values']);
89e45979
MD
126
127 // by passing relationship ID we know that the "B" side is an organization
1ac9bb56
CW
128 $this->assertNotContains($personToPersonType, $options['values']);
129 $this->assertNotContains($personToPersonReverseType, $options['values']);
89e45979 130
1ac9bb56
CW
131 $options = civicrm_api3('Relationship', 'getoptions', [
132 'field' => 'relationship_type_id',
133 'is_form' => TRUE,
39b959db 134 'contact_id' => $individual['id'],
89e45979
MD
135 ]);
136
137 // for this result we only know that "A" must be an individual
1ac9bb56
CW
138 $this->assertContains($personToOrgType, $options['values']);
139 $this->assertNotContains($orgToPersonType, $options['values']);
89e45979
MD
140
141 // unlike when we pass relationship type ID there is no filter by "B" type
1ac9bb56
CW
142 $this->assertContains($personToPersonType, $options['values']);
143 $this->assertContains($personToPersonReverseType, $options['values']);
89e45979
MD
144 }
145
02e028a0
AS
146 /**
147 * Test removeRelationshipTypeDuplicates method.
148 *
149 * @dataProvider getRelationshipTypeDuplicates
150 */
151 public function testRemoveRelationshipTypeDuplicates($relationshipTypeList, $suffix = NULL, $expected, $description) {
152 $result = CRM_Contact_BAO_Relationship::removeRelationshipTypeDuplicates($relationshipTypeList, $suffix);
153 $this->assertEquals($expected, $result, "Failure on set '$description'");
154 }
155
156 public function getRelationshipTypeDuplicates() {
9099cab3 157 $relationshipTypeList = [
02e028a0
AS
158 '1_a_b' => 'duplicate one',
159 '1_b_a' => 'duplicate one',
160 '2_a_b' => 'two a',
161 '2_b_a' => 'two b',
9099cab3
CW
162 ];
163 $data = [
164 [
02e028a0
AS
165 $relationshipTypeList,
166 'a_b',
9099cab3 167 [
02e028a0
AS
168 '1_a_b' => 'duplicate one',
169 '2_a_b' => 'two a',
170 '2_b_a' => 'two b',
9099cab3 171 ],
02e028a0 172 'With suffix a_b',
9099cab3
CW
173 ],
174 [
02e028a0
AS
175 $relationshipTypeList,
176 'b_a',
9099cab3 177 [
02e028a0
AS
178 '1_b_a' => 'duplicate one',
179 '2_a_b' => 'two a',
180 '2_b_a' => 'two b',
9099cab3 181 ],
02e028a0 182 'With suffix b_a',
9099cab3
CW
183 ],
184 [
02e028a0
AS
185 $relationshipTypeList,
186 NULL,
9099cab3 187 [
02e028a0
AS
188 '1_a_b' => 'duplicate one',
189 '2_a_b' => 'two a',
190 '2_b_a' => 'two b',
9099cab3 191 ],
02e028a0 192 'With suffix NULL',
9099cab3
CW
193 ],
194 [
02e028a0
AS
195 $relationshipTypeList,
196 NULL,
9099cab3 197 [
02e028a0
AS
198 '1_a_b' => 'duplicate one',
199 '2_a_b' => 'two a',
200 '2_b_a' => 'two b',
9099cab3 201 ],
02e028a0 202 'With suffix "" (empty string)',
9099cab3
CW
203 ],
204 ];
02e028a0
AS
205 return $data;
206 }
207
96c40ac7 208 /**
209 * Test that two similar memberships are not created for two relationships
210 *
211 * @throws \CRM_Core_Exception
212 */
213 public function testSingleMembershipForTwoRelationships() {
214 $individualID = $this->individualCreate(['display_name' => 'Individual A']);
215 $organisationID = $this->organizationCreate(['organization_name' => 'Organization B']);
216 $membershipOrganisationID = $this->organizationCreate(['organization_name' => 'Membership Organization']);
217 $orgToPersonTypeId1 = $this->relationshipTypeCreate(['name_a_b' => 'Inherited_Relationship_1_A_B', 'name_b_a' => 'Inherited_Relationship_1_B_A']);
218 $orgToPersonTypeId2 = $this->relationshipTypeCreate(['name_a_b' => 'Inherited_Relationship_2_A_B', 'name_b_a' => 'Inherited_Relationship_2_B_A']);
219
220 $membershipType = $this->callAPISuccess('MembershipType', 'create', [
221 'member_of_contact_id' => $membershipOrganisationID,
222 'financial_type_id' => 'Member Dues',
223 'duration_unit' => 'year',
224 'duration_interval' => 1,
225 'period_type' => 'rolling',
226 'name' => 'Inherited Membership',
227 'relationship_type_id' => [$orgToPersonTypeId1, $orgToPersonTypeId2],
1c7c69c5 228 'relationship_direction' => ['b_a', 'b_a'],
96c40ac7 229 ]);
230 $membershipType = $this->callAPISuccessGetSingle('MembershipType', ['id' => $membershipType['id']]);
231 // Check the metadata worked....
232 $this->assertEquals([$orgToPersonTypeId1, $orgToPersonTypeId2], $membershipType['relationship_type_id']);
1c7c69c5 233 $this->assertEquals(['b_a', 'b_a'], $membershipType['relationship_direction']);
96c40ac7 234
0951664a
SL
235 $startDate = date('Y-m') . '-19';
236 $joinDate = date('Y-m', strtotime('1 month ago')) . '-19';
96c40ac7 237 $this->callAPISuccess('Membership', 'create', [
238 'membership_type_id' => $membershipType['id'],
239 'contact_id' => $organisationID,
0951664a
SL
240 'start_date' => $startDate,
241 'join_date' => $joinDate,
96c40ac7 242 ]);
243
244 $relationshipOne = $this->callAPISuccess('Relationship', 'create', [
245 'contact_id_a' => $individualID,
246 'contact_id_b' => $organisationID,
247 'relationship_type_id' => $orgToPersonTypeId1,
248 ]);
1c7c69c5 249 $this->callAPISuccessGetCount('Membership', ['contact_id' => $individualID], 1);
96c40ac7 250 $relationshipTwo = $this->callAPISuccess('Relationship', 'create', [
251 'contact_id_a' => $individualID,
252 'contact_id_b' => $organisationID,
253 'relationship_type_id' => $orgToPersonTypeId2,
254 ]);
1c7c69c5 255 $this->callAPISuccessGetCount('Membership', ['contact_id' => $individualID], 1);
96c40ac7 256
f8569664 257 $inheritedMembership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $individualID]);
0951664a
SL
258 $this->assertEquals($startDate, $inheritedMembership['start_date']);
259 $this->assertEquals($joinDate, $inheritedMembership['join_date']);
f8569664 260
0a2663fd 261 $this->callAPISuccessGetCount('Membership', ['contact_id' => $organisationID], 1);
310c2031 262 // Disable the relationship & check the membership is not removed because the other relationship is still valid.
98fd6fc8 263 $relationshipOne['is_active'] = 0;
264 $this->callAPISuccess('Relationship', 'create', array_merge($relationshipOne, ['is_active' => 0]));
310c2031 265 $this->callAPISuccessGetCount('Membership', ['contact_id' => $individualID], 1);
266
96c40ac7 267 $relationshipTwo['is_active'] = 0;
268 $this->callAPISuccess('Relationship', 'create', $relationshipTwo);
310c2031 269 $this->callAPISuccessGetCount('Membership', ['contact_id' => $individualID], 0);
98fd6fc8 270
96c40ac7 271 $relationshipOne['is_active'] = 1;
272 $this->callAPISuccess('Relationship', 'create', $relationshipOne);
273 $this->callAPISuccessGetCount('Membership', ['contact_id' => $individualID], 1);
310c2031 274
96c40ac7 275 $relationshipTwo['is_active'] = 1;
276 $this->callAPISuccess('Relationship', 'create', $relationshipTwo);
277 $this->callAPISuccessGetCount('Membership', ['contact_id' => $individualID], 1);
278 $this->callAPISuccess('Relationship', 'delete', ['id' => $relationshipTwo['id']]);
279 $this->callAPISuccessGetCount('Membership', ['contact_id' => $individualID], 1);
280 $this->callAPISuccess('Relationship', 'delete', ['id' => $relationshipOne['id']]);
281 $this->callAPISuccessGetCount('Membership', ['contact_id' => $individualID], 0);
6345c936 282
96c40ac7 283 }
284
0d65e601
D
285 /**
286 * Test CRM_Contact_BAO_Relationship::add() function directly.
287 *
288 * In general it's preferred to use the Relationship-create api since it does
289 * checks and such before calling add(). There are already some good tests
290 * for the api, but since it does some more business logic after too the
291 * tests might not be checking exactly the same thing.
292 */
293 public function testBAOAdd() {
294 // add a new type
295 $relationship_type_id_1 = $this->relationshipTypeCreate([
296 'name_a_b' => 'Food poison tester is',
297 'name_b_a' => 'Food poison tester for',
298 'contact_type_a' => 'Individual',
299 'contact_type_b' => 'Individual',
300 ]);
301
302 // add some people
303 $contact_id_1 = $this->individualCreate();
304 $contact_id_2 = $this->individualCreate([], 1);
305
306 // create new relationship (using BAO)
307 $params = [
308 'relationship_type_id' => $relationship_type_id_1,
309 'contact_id_a' => $contact_id_1,
310 'contact_id_b' => $contact_id_2,
311 ];
312 $relationshipObj = CRM_Contact_BAO_Relationship::add($params);
313 $this->assertEquals($relationshipObj->relationship_type_id, $relationship_type_id_1);
314 $this->assertEquals($relationshipObj->contact_id_a, $contact_id_1);
315 $this->assertEquals($relationshipObj->contact_id_b, $contact_id_2);
316 $this->assertEquals($relationshipObj->is_active, 1);
317
318 // demonstrate PR 15103 - should fail before the patch and pass after
319 $today = date('Ymd');
320 $params = [
321 'id' => $relationshipObj->id,
322 'end_date' => $today,
323 ];
324 $relationshipObj = CRM_Contact_BAO_Relationship::add($params);
325 $this->assertEquals($relationshipObj->relationship_type_id, $relationship_type_id_1);
326 $this->assertEquals($relationshipObj->contact_id_a, $contact_id_1);
327 $this->assertEquals($relationshipObj->contact_id_b, $contact_id_2);
328 $this->assertEquals($relationshipObj->is_active, 1);
329 $this->assertEquals($relationshipObj->end_date, $today);
330 }
331
02e028a0 332}