Merge pull request #17641 from MegaphoneJon/core-1590
[civicrm-core.git] / tests / phpunit / api / v3 / MembershipTest.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 use Civi\Api4\MembershipType;
13 use Civi\Api4\Relationship;
14 use Civi\Api4\RelationshipType;
15
16 /**
17 * Test APIv3 civicrm_membership functions
18 *
19 * @package CiviCRM_APIv3
20 * @subpackage API_Member
21 */
22
23 /**
24 * Class api_v3_MembershipTest
25 * @group headless
26 */
27 class api_v3_MembershipTest extends CiviUnitTestCase {
28
29 use CRMTraits_Financial_OrderTrait;
30
31 protected $_contactID;
32 protected $_membershipID;
33 protected $_membershipID2;
34 protected $_membershipID3;
35 protected $_membershipTypeID;
36 protected $_membershipTypeID2;
37 protected $_membershipStatusID;
38 protected $_entity;
39 protected $_params;
40
41 /**
42 * Set up for tests.
43 */
44 public function setUp() {
45 parent::setUp();
46 $this->_contactID = $this->individualCreate();
47 $this->_membershipTypeID = $this->membershipTypeCreate(['member_of_contact_id' => $this->_contactID]);
48 $this->_membershipTypeID2 = $this->membershipTypeCreate([
49 'period_type' => 'fixed',
50 // Ie. 1 March.
51 'fixed_period_start_day' => '301',
52 // Ie. 11 Nov.
53 'fixed_period_rollover_day' => '1111',
54 'name' => 'Another one',
55 ]);
56 $this->_membershipStatusID = $this->membershipStatusCreate('test status');
57
58 CRM_Member_PseudoConstant::membershipStatus(NULL, NULL, 'name', TRUE);
59 CRM_Core_PseudoConstant::activityType(TRUE, TRUE, TRUE, 'name');
60
61 $this->_entity = 'Membership';
62 $this->_params = [
63 'contact_id' => $this->_contactID,
64 'membership_type_id' => $this->_membershipTypeID,
65 'join_date' => '2009-01-21',
66 'start_date' => '2009-01-21',
67 'end_date' => '2009-12-21',
68 'source' => 'Payment',
69 'is_override' => 1,
70 'status_id' => $this->_membershipStatusID,
71 ];
72 }
73
74 /**
75 * Clean up after tests.
76 *
77 * @throws \Exception
78 */
79 public function tearDown() {
80 $this->quickCleanUpFinancialEntities();
81 $this->quickCleanup(['civicrm_uf_match'], TRUE);
82 $this->contactDelete($this->_contactID);
83 parent::tearDown();
84 }
85
86 /**
87 * Test membership deletion.
88 */
89 public function testMembershipDelete() {
90 $membershipID = $this->contactMembershipCreate($this->_params);
91 $this->assertDBRowExist('CRM_Member_DAO_Membership', $membershipID);
92 $params = [
93 'id' => $membershipID,
94 ];
95 $this->callAPIAndDocument('membership', 'delete', $params, __FUNCTION__, __FILE__);
96 $this->assertDBRowNotExist('CRM_Member_DAO_Membership', $membershipID);
97 }
98
99 public function testMembershipDeleteEmpty() {
100 $this->callAPIFailure('membership', 'delete', []);
101 }
102
103 public function testMembershipDeleteInvalidID() {
104 $this->callAPIFailure('membership', 'delete', ['id' => 'blah']);
105 }
106
107 /**
108 * Test membership deletion and with the preserve contribution param.
109 */
110 public function testMembershipDeletePreserveContribution() {
111 //DELETE
112 $membershipID = $this->contactMembershipCreate($this->_params);
113 //DELETE
114 $this->assertDBRowExist('CRM_Member_DAO_Membership', $membershipID);
115 $ContributionCreate = $this->callAPISuccess('Contribution', 'create', [
116 'sequential' => 1,
117 'financial_type_id' => "Member Dues",
118 'total_amount' => 100,
119 'contact_id' => $this->_params['contact_id'],
120 ]);
121 $this->callAPISuccess('MembershipPayment', 'create', [
122 'sequential' => 1,
123 'contribution_id' => $ContributionCreate['values'][0]['id'],
124 'membership_id' => $membershipID,
125 ]);
126 $memParams = [
127 'id' => $membershipID,
128 'preserve_contribution' => 1,
129 ];
130 $contribParams = [
131 'id' => $ContributionCreate['values'][0]['id'],
132 ];
133 $this->callAPIAndDocument('membership', 'delete', $memParams, __FUNCTION__, __FILE__);
134 $this->assertDBRowNotExist('CRM_Member_DAO_Membership', $membershipID);
135 $this->assertDBRowExist('CRM_Contribute_DAO_Contribution', $ContributionCreate['values'][0]['id']);
136 $this->callAPISuccess('Contribution', 'delete', $contribParams);
137 $this->assertDBRowNotExist('CRM_Contribute_DAO_Contribution', $ContributionCreate['values'][0]['id']);
138 }
139
140 /**
141 * Test Activity creation on cancellation of membership contribution.
142 */
143 public function testActivityForCancelledContribution() {
144 $contactId = $this->createLoggedInUser();
145 $membershipID = $this->contactMembershipCreate($this->_params);
146
147 $ContributionCreate = $this->callAPISuccess('Contribution', 'create', [
148 'financial_type_id' => 'Member Dues',
149 'total_amount' => 100,
150 'contact_id' => $this->_params['contact_id'],
151 ]);
152 $this->callAPISuccess('MembershipPayment', 'create', [
153 'sequential' => 1,
154 'contribution_id' => $ContributionCreate['id'],
155 'membership_id' => $membershipID,
156 ]);
157
158 $form = new CRM_Contribute_Form_Contribution();
159 $form->_id = $ContributionCreate['id'];
160 $form->testSubmit([
161 'total_amount' => 100,
162 'financial_type_id' => 1,
163 'contact_id' => $contactId,
164 'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
165 'contribution_status_id' => 3,
166 ],
167 CRM_Core_Action::UPDATE);
168
169 $this->callAPISuccessGetSingle('Activity', [
170 'activity_type_id' => 'Membership Signup',
171 'source_record_id' => $membershipID,
172 'subject' => 'General - Payment - Status: test status',
173 ]);
174 $this->callAPISuccessGetSingle('Activity', [
175 'activity_type_id' => 'Change Membership Status',
176 'source_record_id' => $membershipID,
177 ]);
178 }
179
180 /**
181 * Test Multiple Membership Status for same contribution id.
182 */
183 public function testMultipleMembershipsContribution() {
184 // Main contact
185 $memStatus = CRM_Member_PseudoConstant::membershipStatus();
186 // Pending Membership Status
187 $pendingMembershipId = array_search('Pending', $memStatus);
188 // New Membership Status
189 $newMembershipId = array_search('test status', $memStatus);
190
191 $membershipParam = [
192 'membership_type_id' => $this->_membershipTypeID,
193 'source' => 'Webform Payment',
194 'status_id' => $pendingMembershipId,
195 'is_pay_later' => 1,
196 'skipStatusCal' => 1,
197 ];
198
199 // Contact 1
200 $contactId1 = $this->individualCreate();
201 $membershipParam['contact_id'] = $contactId1;
202 $membershipID1 = $this->contactMembershipCreate($membershipParam);
203
204 // Pending Payment Status
205 $ContributionCreate = $this->callAPISuccess('Contribution', 'create', [
206 'financial_type_id' => '1',
207 'total_amount' => 100,
208 'contact_id' => $contactId1,
209 'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
210 'contribution_status_id' => 2,
211 'is_pay_later' => 1,
212 'receive_date' => date('Ymd'),
213 ]);
214 $this->callAPISuccess('MembershipPayment', 'create', [
215 'sequential' => 1,
216 'contribution_id' => $ContributionCreate['id'],
217 'membership_id' => $membershipID1,
218 ]);
219
220 // Contact 2
221 $contactId2 = $this->individualCreate();
222 $membershipParam['contact_id'] = $contactId2;
223 $membershipID2 = $this->contactMembershipCreate($membershipParam);
224 $this->callAPISuccess('MembershipPayment', 'create', [
225 'sequential' => 1,
226 'contribution_id' => $ContributionCreate['id'],
227 'membership_id' => $membershipID2,
228 ]);
229
230 // Contact 3
231 $contactId3 = $this->individualCreate();
232 $membershipParam['contact_id'] = $contactId3;
233 $membershipID3 = $this->contactMembershipCreate($membershipParam);
234 $this->callAPISuccess('MembershipPayment', 'create', [
235 'sequential' => 1,
236 'contribution_id' => $ContributionCreate['id'],
237 'membership_id' => $membershipID3,
238 ]);
239
240 // Change Payment Status to Completed
241 $form = new CRM_Contribute_Form_Contribution();
242 $form->_id = $ContributionCreate['id'];
243 $params = ['id' => $ContributionCreate['id']];
244 $values = $ids = [];
245 CRM_Contribute_BAO_Contribution::getValues($params, $values, $ids);
246 $form->_values = $values;
247 $form->testSubmit([
248 'total_amount' => 100,
249 'financial_type_id' => '1',
250 'contact_id' => $contactId1,
251 'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
252 'contribution_status_id' => 1,
253 ],
254 CRM_Core_Action::UPDATE);
255
256 // check for Membership 1
257 $params = ['id' => $membershipID1];
258 $membership1 = $this->callAPISuccess('membership', 'get', $params);
259 $result1 = $membership1['values'][$membershipID1];
260 $this->assertEquals($result1['contact_id'], $contactId1);
261 $this->assertEquals($result1['status_id'], $newMembershipId);
262
263 // check for Membership 2
264 $params = ['id' => $membershipID2];
265 $membership2 = $this->callAPISuccess('membership', 'get', $params);
266 $result2 = $membership2['values'][$membershipID2];
267 $this->assertEquals($result2['contact_id'], $contactId2);
268 $this->assertEquals($result2['status_id'], $newMembershipId);
269
270 // check for Membership 3
271 $params = ['id' => $membershipID3];
272 $membership3 = $this->callAPISuccess('membership', 'get', $params);
273 $result3 = $membership3['values'][$membershipID3];
274 $this->assertEquals($result3['contact_id'], $contactId3);
275 $this->assertEquals($result3['status_id'], $newMembershipId);
276 }
277
278 /**
279 * Test membership get.
280 */
281 public function testContactMembershipsGet() {
282 $this->_membershipID = $this->contactMembershipCreate($this->_params);
283 $this->callAPISuccess('membership', 'get', []);
284 $this->callAPISuccess('Membership', 'Delete', ['id' => $this->_membershipID]);
285 }
286
287 /**
288 * Test civicrm_membership_get with params not array.
289 *
290 * Gets treated as contact_id, memberships expected.
291 */
292 public function testGetWithParamsContactId() {
293 $this->_membershipID = $this->contactMembershipCreate($this->_params);
294 $params = [
295 'contact_id' => $this->_contactID,
296 ];
297 $membership = $this->callAPISuccess('membership', 'get', $params);
298
299 $result = $membership['values'][$this->_membershipID];
300 $this->callAPISuccess('Membership', 'Delete', [
301 'id' => $this->_membershipID,
302 ]);
303 $this->assertEquals($result['contact_id'], $this->_contactID);
304 $this->assertEquals($result['membership_type_id'], $this->_membershipTypeID);
305 $this->assertEquals($result['status_id'], $this->_membershipStatusID);
306 $this->assertEquals($result['join_date'], '2009-01-21');
307 $this->assertEquals($result['start_date'], '2009-01-21');
308 $this->assertEquals($result['end_date'], '2009-12-21');
309 $this->assertEquals($result['source'], 'Payment');
310 $this->assertEquals($result['is_override'], 1);
311 }
312
313 /**
314 * Test civicrm_membership_get with params not array.
315 *
316 * Gets treated as contact_id, memberships expected.
317 */
318 public function testGetInSyntax() {
319 $this->_membershipID = $this->contactMembershipCreate($this->_params);
320 $this->_membershipID2 = $this->contactMembershipCreate($this->_params);
321 $this->_membershipID3 = $this->contactMembershipCreate($this->_params);
322 $params = [
323 'id' => ['IN' => [$this->_membershipID, $this->_membershipID3]],
324 ];
325 $membership = $this->callAPISuccess('membership', 'get', $params);
326 $this->assertEquals(2, $membership['count']);
327 $this->assertEquals([$this->_membershipID, $this->_membershipID3], array_keys($membership['values']));
328 $params = [
329 'id' => ['NOT IN' => [$this->_membershipID, $this->_membershipID3]],
330 ];
331 $membership = $this->callAPISuccess('membership', 'get', $params);
332 $this->assertEquals(1, $membership['count']);
333 $this->assertEquals([$this->_membershipID2], array_keys($membership['values']));
334 }
335
336 /**
337 * Test civicrm_membership_get with params not array.
338 * Gets treated as contact_id, memberships expected.
339 */
340 public function testGetInSyntaxOnContactID() {
341 $this->_membershipID = $this->contactMembershipCreate($this->_params);
342 $contact2 = $this->individualCreate();
343 $contact3 = $this->individualCreate(['first_name' => 'Scout', 'last_name' => 'Canine']);
344 $this->_membershipID2 = $this->contactMembershipCreate(array_merge($this->_params, ['contact_id' => $contact2]));
345 $this->_membershipID3 = $this->contactMembershipCreate(array_merge($this->_params, ['contact_id' => $contact3]));
346 $params = [
347 'contact_id' => ['IN' => [$this->_contactID, $contact3]],
348 ];
349 $membership = $this->callAPISuccess('membership', 'get', $params);
350 $this->assertEquals(2, $membership['count']);
351 $this->assertEquals([$this->_membershipID, $this->_membershipID3], array_keys($membership['values']));
352 $params = [
353 'contact_id' => ['NOT IN' => [$this->_contactID, $contact3]],
354 ];
355 $membership = $this->callAPISuccess('membership', 'get', $params);
356 $this->assertEquals(1, $membership['count']);
357 $this->assertEquals([$this->_membershipID2], array_keys($membership['values']));
358 }
359
360 /**
361 * Test civicrm_membership_get with params not array.
362 *
363 * Gets treated as contact_id, memberships expected.
364 */
365 public function testGetWithParamsMemberShipTypeId() {
366 $this->callAPISuccess($this->_entity, 'create', $this->_params);
367 $params = [
368 'membership_type_id' => $this->_membershipTypeID,
369 ];
370 $membership = $this->callAPISuccess('membership', 'get', $params);
371 $this->callAPISuccess('Membership', 'Delete', [
372 'id' => $membership['id'],
373 ]);
374 $result = $membership['values'][$membership['id']];
375 $this->assertEquals($result['contact_id'], $this->_contactID);
376 $this->assertEquals($result['membership_type_id'], $this->_membershipTypeID);
377 $this->assertEquals($result['status_id'], $this->_membershipStatusID);
378 $this->assertEquals($result['join_date'], '2009-01-21');
379 $this->assertEquals($result['start_date'], '2009-01-21');
380 $this->assertEquals($result['end_date'], '2009-12-21');
381 $this->assertEquals($result['source'], 'Payment');
382 $this->assertEquals($result['is_override'], 1);
383 $this->assertEquals($result['id'], $membership['id']);
384 }
385
386 /**
387 * Test civicrm_membership_get with params not array.
388 * Gets treated as contact_id, memberships expected.
389 */
390 public function testGetWithParamsMemberShipTypeIdContactID() {
391 $params = $this->_params;
392 $this->callAPISuccess($this->_entity, 'create', $params);
393 $params['membership_type_id'] = $this->_membershipTypeID2;
394 $this->callAPISuccess($this->_entity, 'create', $params);
395 $this->callAPISuccessGetCount('membership', ['contact_id' => $this->_contactID], 2);
396 $params = [
397 'membership_type_id' => $this->_membershipTypeID,
398 'contact_id' => $this->_contactID,
399 ];
400 $result = $this->callAPISuccess('membership', 'getsingle', $params);
401 $this->assertEquals($result['contact_id'], $this->_contactID);
402 $this->assertEquals($result['membership_type_id'], $this->_membershipTypeID);
403
404 $params = [
405 'membership_type_id' => $this->_membershipTypeID2,
406 'contact_id' => $this->_contactID,
407 ];
408 $result = $this->callAPISuccess('membership', 'getsingle', $params);
409 $this->assertEquals($result['contact_id'], $this->_contactID);
410 $this->assertEquals($result['membership_type_id'], $this->_membershipTypeID2);
411 }
412
413 /**
414 * Check with complete array + custom field.
415 *
416 * Note that the test is written on purpose without any
417 * variables specific to participant so it can be replicated into other entities
418 * and / or moved to the automated test suite
419 */
420 public function testGetWithParamsMemberShipIdAndCustom() {
421 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
422
423 $params = $this->_params;
424 $params['custom_' . $ids['custom_field_id']] = "custom string";
425
426 $result = $this->callAPISuccess($this->_entity, 'create', $params);
427
428 $getParams = ['membership_type_id' => $params['membership_type_id']];
429 $check = $this->callAPIAndDocument($this->_entity, 'get', $getParams, __FUNCTION__, __FILE__);
430 $this->assertEquals("custom string", $check['values'][$result['id']]['custom_' . $ids['custom_field_id']], ' in line ' . __LINE__);
431
432 $this->callAPISuccess('Membership', 'Delete', [
433 'id' => $result['id'],
434 ]);
435 }
436
437 /**
438 * Test civicrm_membership_get with proper params.
439 * Memberships expected.
440 */
441 public function testGet() {
442 $membershipID = $this->contactMembershipCreate($this->_params);
443 $params = [
444 'contact_id' => $this->_contactID,
445 ];
446
447 $membership = $this->callAPISuccess('membership', 'get', $params);
448 $result = $membership['values'][$membershipID];
449 $this->callAPISuccess('Membership', 'Delete', [
450 'id' => $membership['id'],
451 ]);
452 $this->assertEquals($result['join_date'], '2009-01-21');
453 $this->assertEquals($result['contact_id'], $this->_contactID);
454 $this->assertEquals($result['membership_type_id'], $this->_membershipTypeID);
455 $this->assertEquals($result['status_id'], $this->_membershipStatusID);
456
457 $this->assertEquals($result['start_date'], '2009-01-21');
458 $this->assertEquals($result['end_date'], '2009-12-21');
459 $this->assertEquals($result['source'], 'Payment');
460 $this->assertEquals($result['is_override'], 1);
461 }
462
463 /**
464 * Test civicrm_membership_get with proper params.
465 * Memberships expected.
466 */
467 public function testGetWithId() {
468 $membershipID = $this->contactMembershipCreate($this->_params);
469 $params = [
470 'contact_id' => $this->_contactID,
471 'id' => $this->_membershipID,
472 'return' => 'id',
473 ];
474 $result = $this->callAPISuccess('membership', 'get', $params);
475 $this->assertEquals($membershipID, $result['id']);
476 $params = [
477 'contact_id' => $this->_contactID,
478 'membership_id' => $this->_membershipID,
479 'return' => 'membership_id',
480 ];
481 $result = $this->callAPISuccess('membership', 'get', $params);
482 $this->assertEquals($membershipID, $result['id']);
483 }
484
485 /**
486 * Test civicrm_membership_get for only active.
487 * Memberships expected.
488 */
489 public function testGetOnlyActive() {
490 $description = "Demonstrates use of 'filter' active_only' param.";
491 $this->_membershipID = $this->contactMembershipCreate($this->_params);
492 $params = [
493 'contact_id' => $this->_contactID,
494 'active_only' => 1,
495 ];
496
497 $membership = $this->callAPISuccess('membership', 'get', $params);
498 $this->assertEquals($membership['values'][$this->_membershipID]['status_id'], $this->_membershipStatusID);
499 $this->assertEquals($membership['values'][$this->_membershipID]['contact_id'], $this->_contactID);
500 $params = [
501 'contact_id' => $this->_contactID,
502 'filters' => [
503 'is_current' => 1,
504 ],
505 ];
506
507 $membership = $this->callAPIAndDocument('membership', 'get', $params, __FUNCTION__, __FILE__, $description, 'FilterIsCurrent');
508 $this->assertEquals($membership['values'][$this->_membershipID]['status_id'], $this->_membershipStatusID);
509 $this->assertEquals($membership['values'][$this->_membershipID]['contact_id'], $this->_contactID);
510
511 $this->callAPISuccess('Membership', 'Delete', ['id' => $this->_membershipID]);
512 }
513
514 /**
515 * Test civicrm_membership_get for non exist contact.
516 * empty Memberships.
517 */
518 public function testGetNoContactExists() {
519 $params = [
520 'contact_id' => 55555,
521 ];
522
523 $membership = $this->callAPISuccess('membership', 'get', $params);
524 $this->assertEquals($membership['count'], 0);
525 }
526
527 /**
528 * Test civicrm_membership_get with relationship.
529 * get Memberships.
530 *
531 * @throws \CRM_Core_Exception
532 */
533 public function testGetWithRelationship() {
534 $membershipOrgId = $this->organizationCreate(NULL);
535 $memberContactId = $this->individualCreate();
536
537 $relTypeParams = [
538 'name_a_b' => 'Relation 1',
539 'name_b_a' => 'Relation 2',
540 'description' => 'Testing relationship type',
541 'contact_type_a' => 'Organization',
542 'contact_type_b' => 'Individual',
543 'is_reserved' => 1,
544 'is_active' => 1,
545 ];
546 $relTypeID = $this->relationshipTypeCreate($relTypeParams);
547
548 $params = [
549 'name' => 'test General',
550 'duration_unit' => 'year',
551 'duration_interval' => 1,
552 'period_type' => 'rolling',
553 'member_of_contact_id' => $membershipOrgId,
554 'domain_id' => 1,
555 'financial_type_id' => 1,
556 'relationship_type_id' => $relTypeID,
557 'relationship_direction' => 'b_a',
558 'is_active' => 1,
559 ];
560 $memType = $this->callAPISuccess('membership_type', 'create', $params);
561
562 $params = [
563 'contact_id' => $memberContactId,
564 'membership_type_id' => $memType['id'],
565 'join_date' => '2009-01-21',
566 'start_date' => '2009-01-21',
567 'end_date' => '2009-12-21',
568 'source' => 'Payment',
569 'is_override' => 1,
570 'status_id' => $this->_membershipStatusID,
571 ];
572 $membershipID = $this->contactMembershipCreate($params);
573
574 $params = [
575 'contact_id' => $memberContactId,
576 'membership_type_id' => $memType['id'],
577 ];
578
579 $result = $this->callAPISuccess('membership', 'get', $params);
580
581 $membership = $result['values'][$membershipID];
582 $this->assertEquals($this->_membershipStatusID, $membership['status_id']);
583 $this->callAPISuccess('Membership', 'Delete', [
584 'id' => $membership['id'],
585 ]);
586 $this->membershipTypeDelete(['id' => $memType['id']]);
587 $this->relationshipTypeDelete($relTypeID);
588 $this->contactDelete($membershipOrgId);
589 $this->contactDelete($memberContactId);
590 }
591
592 /**
593 * Test civicrm_membership_create with relationships.
594 * create/get Memberships.
595 *
596 * Test suite for CRM-14758: API ( contact, create ) does not always create related membership
597 * and max_related property for Membership_Type and Membership entities
598 *
599 * @throws \CRM_Core_Exception
600 */
601 public function testCreateWithRelationship() {
602 // Create membership type: inherited through employment, max_related = 2
603 $params = [
604 'name_a_b' => 'Employee of',
605 ];
606 $result = $this->callAPISuccess('relationship_type', 'get', $params);
607 $relationshipTypeId = $result['id'];
608 $membershipOrgId = $this->organizationCreate();
609 $params = [
610 'name' => 'Corporate Membership',
611 'duration_unit' => 'year',
612 'duration_interval' => 1,
613 'period_type' => 'rolling',
614 'member_of_contact_id' => $membershipOrgId,
615 'domain_id' => 1,
616 'financial_type_id' => 1,
617 'relationship_type_id' => $relationshipTypeId,
618 'relationship_direction' => 'b_a',
619 'max_related' => 2,
620 'is_active' => 1,
621 ];
622 $result = $this->callAPISuccess('membership_type', 'create', $params);
623 $membershipTypeId = $result['id'];
624
625 // Create employer and first employee
626 $employerId[0] = $this->organizationCreate([], 1);
627 $memberContactId[0] = $this->individualCreate(['employer_id' => $employerId[0]], 0);
628
629 // Create organization's membership
630 $params = [
631 'contact_id' => $employerId[0],
632 'membership_type_id' => $membershipTypeId,
633 'source' => 'Test suite',
634 'start_date' => date('Y-m-d'),
635 'end_date' => '+1 year',
636 ];
637 $OrganizationMembershipID = $this->contactMembershipCreate($params);
638
639 // Check that the employee inherited the membership
640 $params = [
641 'contact_id' => $memberContactId[0],
642 'membership_type_id' => $membershipTypeId,
643 ];
644
645 $result = $this->callAPISuccess('membership', 'get', $params);
646
647 $this->assertEquals(1, $result['count']);
648 $result = $result['values'][$result['id']];
649 $this->assertEquals($OrganizationMembershipID, $result['owner_membership_id']);
650
651 // Create second employee
652 $memberContactId[1] = $this->individualCreate(['employer_id' => $employerId[0]], 1);
653
654 // Check that the employee inherited the membership
655 $params = [
656 'contact_id' => $memberContactId[1],
657 'membership_type_id' => $membershipTypeId,
658 ];
659 $result = $this->callAPISuccess('membership', 'get', $params);
660 // If it fails here CRM-14758 is not fixed
661 $this->assertEquals(1, $result['count']);
662 $result = $result['values'][$result['id']];
663 $this->assertEquals($OrganizationMembershipID, $result['owner_membership_id']);
664
665 // Create third employee
666 $memberContactId[2] = $this->individualCreate(['employer_id' => $employerId[0]], 2);
667
668 // Check that employee does NOT inherit the membership (max_related = 2)
669 $params = [
670 'contact_id' => $memberContactId[2],
671 'membership_type_id' => $membershipTypeId,
672 ];
673 $result = $this->callAPISuccess('membership', 'get', $params);
674 $this->assertEquals(0, $result['count']);
675
676 // Increase max_related for the employer's membership
677 $params = [
678 'id' => $OrganizationMembershipID,
679 'max_related' => 3,
680 ];
681 $this->callAPISuccess('Membership', 'create', $params);
682
683 // Check that the employee inherited the membership
684 $params = [
685 'contact_id' => $memberContactId[2],
686 'membership_type_id' => $membershipTypeId,
687 ];
688 $result = $this->callAPISuccess('membership', 'get', $params);
689 $this->assertEquals(1, $result['count']);
690 $result = $result['values'][$result['id']];
691 $this->assertEquals($OrganizationMembershipID, $result['owner_membership_id']);
692
693 // First employee moves to a new job
694 $employerId[1] = $this->organizationCreate([], 2);
695 $params = [
696 'id' => $memberContactId[0],
697 'employer_id' => $employerId[1],
698 ];
699 $this->callAPISuccess('contact', 'create', $params);
700
701 // Check that employee does NO LONGER inherit the membership
702 $params = [
703 'contact_id' => $memberContactId[0],
704 'membership_type_id' => $membershipTypeId,
705 ];
706 $result = $this->callAPISuccess('membership', 'get', $params);
707 $this->assertEquals(0, $result['count']);
708
709 //Create pay_later membership for organization.
710 $employerId[2] = $this->organizationCreate([], 1);
711 $params = [
712 'contact_id' => $employerId[2],
713 'membership_type_id' => $membershipTypeId,
714 'source' => 'Test pay later suite',
715 'is_pay_later' => 1,
716 'status_id' => 5,
717 ];
718 $organizationMembershipID = $this->callAPISuccess('Membership', 'create', $params)['id'];
719
720 $memberContactId[3] = $this->individualCreate(['employer_id' => $employerId[2]], 0);
721 // Check that the employee inherited the membership
722 $params = [
723 'contact_id' => $memberContactId[3],
724 'membership_type_id' => $membershipTypeId,
725 ];
726 $result = $this->callAPISuccessGetSingle('membership', $params);
727 $this->assertEquals($organizationMembershipID, $result['owner_membership_id']);
728
729 // Set up params for enable/disable checks
730 $relationship1 = $this->callAPISuccess('relationship', 'get', ['contact_id_a' => $memberContactId[1]]);
731 $params = [
732 'contact_id' => $memberContactId[1],
733 'membership_type_id' => $membershipTypeId,
734 ];
735
736 // Deactivate relationship using create and assert membership is not inherited
737 $this->callAPISuccess('relationship', 'create', ['id' => $relationship1['id'], 'is_active' => 0]);
738 $result = $this->callAPISuccess('membership', 'get', $params);
739 $this->assertEquals(0, $result['count']);
740
741 // Re-enable relationship using create and assert membership is inherited
742 $this->callAPISuccess('relationship', 'create', ['id' => $relationship1['id'], 'is_active' => 1]);
743 $result = $this->callAPISuccess('membership', 'get', $params);
744 $this->assertEquals(1, $result['count']);
745
746 // Deactivate relationship using setvalue and assert membership is not inherited
747 $this->callAPISuccess('relationship', 'setvalue', ['id' => $relationship1['id'], 'field' => 'is_active', 'value' => 0]);
748 $result = $this->callAPISuccess('membership', 'get', $params);
749 $this->assertEquals(0, $result['count']);
750
751 // Re-enable relationship using setvalue and assert membership is inherited
752 $this->callAPISuccess('relationship', 'setvalue', ['id' => $relationship1['id'], 'field' => 'is_active', 'value' => 1]);
753 $result = $this->callAPISuccess('membership', 'get', $params);
754 $this->assertEquals(1, $result['count']);
755
756 // Delete relationship and assert membership is not inherited
757 $this->callAPISuccess('relationship', 'delete', ['id' => $relationship1['id']]);
758 $result = $this->callAPISuccess('membership', 'get', $params);
759 $this->assertEquals(0, $result['count']);
760
761 // Tear down - reverse of creation to be safe
762 $this->contactDelete($memberContactId[2]);
763 $this->contactDelete($memberContactId[1]);
764 $this->contactDelete($memberContactId[0]);
765 $this->contactDelete($employerId[1]);
766 $this->contactDelete($employerId[0]);
767 $this->membershipTypeDelete(['id' => $membershipTypeId]);
768 $this->contactDelete($membershipOrgId);
769 }
770
771 /**
772 * Test that loops are not created when adding spouse relationships.
773 *
774 * This add a test for https://issues.civicrm.org/jira/browse/CRM-4213 in the hope of removing
775 * the buggy fix for that without a resurgence.
776 *
777 * @throws \API_Exception
778 * @throws \CRM_Core_Exception
779 * @throws \Civi\API\Exception\UnauthorizedException
780 */
781 public function testCreateWithSpouseRelationship() {
782 $relationshipTypeID = RelationshipType::get()->addSelect('id')->addWhere('name_a_b', '=', 'Spouse of')->execute()->first()['id'];
783 MembershipType::update()->setValues([
784 'relationship_direction' => ['b_a', 'a_b'],
785 'relationship_type_id' => [$relationshipTypeID, $relationshipTypeID],
786 ])
787 ->addWhere('name', '=', 'General')
788 ->execute()->first()['id'];
789
790 $spouse1ID = $this->individualCreate(['first_name' => 'him']);
791 $spouse2ID = $this->individualCreate(['first_name' => 'her']);
792 $spouse3ID = $this->individualCreate(['first_name' => 'they']);
793 $spouse4ID = $this->individualCreate(['first_name' => 'them']);
794 Relationship::create()->setValues([
795 'contact_id_a' => $spouse1ID,
796 'contact_id_b' => $spouse2ID,
797 'relationship_type_id' => $relationshipTypeID,
798 ])->execute();
799
800 $this->contactMembershipCreate([
801 'contact_id' => $spouse1ID,
802 'start_date' => date('Y-m-d'),
803 'end_date' => '+1 year',
804 ]);
805
806 $this->callAPISuccessGetSingle('Membership', [
807 'contact_id' => $spouse2ID,
808 'membership_type_id' => 'General',
809 ]);
810
811 $this->callAPISuccessGetSingle('Membership', [
812 'contact_id' => $spouse1ID,
813 'membership_type_id' => 'General',
814 ]);
815 // Add another Spouse
816 Relationship::create()->setValues([
817 'contact_id_a' => $spouse3ID,
818 'contact_id_b' => $spouse1ID,
819 'relationship_type_id' => $relationshipTypeID,
820 ])->execute();
821 $this->callAPISuccessGetSingle('Membership', [
822 'contact_id' => $spouse3ID,
823 'membership_type_id' => 'General',
824 ]);
825 $this->callAPISuccessGetCount('Membership', [], 3);
826 Relationship::create()->setValues([
827 'contact_id_a' => $spouse1ID,
828 'contact_id_b' => $spouse4ID,
829 'relationship_type_id' => $relationshipTypeID,
830 ])->execute();
831
832 $this->callAPISuccessGetSingle('Membership', [
833 'contact_id' => $spouse4ID,
834 'membership_type_id' => 'General',
835 ]);
836
837 $this->callAPISuccessGetCount('Membership', [], 4);
838 }
839
840 /**
841 * We are checking for no e-notices + only id & end_date returned
842 *
843 * @throws \CRM_Core_Exception
844 */
845 public function testMembershipGetWithReturn() {
846 $this->contactMembershipCreate($this->_params);
847 $result = $this->callAPISuccess('membership', 'get', ['return' => 'end_date']);
848 foreach ($result['values'] as $membership) {
849 $this->assertEquals(['id', 'end_date'], array_keys($membership));
850 }
851 }
852
853 ///////////////// civicrm_membership_create methods
854
855 /**
856 * Test civicrm_contact_memberships_create with empty params.
857 * Error expected.
858 */
859 public function testCreateWithEmptyParams() {
860 $params = [];
861 $this->callAPIFailure('membership', 'create', $params);
862 }
863
864 /**
865 * If is_overide is passed in status must also be passed in.
866 */
867 public function testCreateOverrideNoStatus() {
868 $params = $this->_params;
869 unset($params['status_id']);
870 $this->callAPIFailure('membership', 'create', $params);
871 }
872
873 public function testMembershipCreateMissingRequired() {
874 $params = [
875 'membership_type_id' => '1',
876 'join_date' => '2006-01-21',
877 'start_date' => '2006-01-21',
878 'end_date' => '2006-12-21',
879 'source' => 'Payment',
880 'status_id' => '2',
881 ];
882
883 $this->callAPIFailure('membership', 'create', $params);
884 }
885
886 public function testMembershipCreate() {
887 $params = [
888 'contact_id' => $this->_contactID,
889 'membership_type_id' => $this->_membershipTypeID,
890 'join_date' => '2006-01-21',
891 'start_date' => '2006-01-21',
892 'end_date' => '2006-12-21',
893 'source' => 'Payment',
894 'is_override' => 1,
895 'status_id' => $this->_membershipStatusID,
896 ];
897
898 $result = $this->callAPIAndDocument('membership', 'create', $params, __FUNCTION__, __FILE__);
899 $this->getAndCheck($params, $result['id'], $this->_entity);
900 $this->assertNotNull($result['id']);
901 $this->assertEquals($this->_contactID, $result['values'][$result['id']]['contact_id'], " in line " . __LINE__);
902 $this->assertEquals($result['id'], $result['values'][$result['id']]['id'], " in line " . __LINE__);
903 }
904
905 /**
906 * Check for useful message if contact doesn't exist
907 */
908 public function testMembershipCreateWithInvalidContact() {
909 $params = [
910 'contact_id' => 999,
911 'membership_type_id' => $this->_membershipTypeID,
912 'join_date' => '2006-01-21',
913 'start_date' => '2006-01-21',
914 'end_date' => '2006-12-21',
915 'source' => 'Payment',
916 'is_override' => 1,
917 'status_id' => $this->_membershipStatusID,
918 ];
919
920 $this->callAPIFailure('membership', 'create', $params,
921 'contact_id is not valid : 999'
922 );
923 }
924
925 public function testMembershipCreateWithInvalidStatus() {
926 $params = $this->_params;
927 $params['status_id'] = 999;
928 $this->callAPIFailure('membership', 'create', $params,
929 "'999' is not a valid option for field status_id"
930 );
931 }
932
933 public function testMembershipCreateWithInvalidType() {
934 $params = $this->_params;
935 $params['membership_type_id'] = 999;
936
937 $this->callAPIFailure('membership', 'create', $params,
938 "'999' is not a valid option for field membership_type_id"
939 );
940 }
941
942 /**
943 * Check with complete array + custom field
944 * Note that the test is written on purpose without any
945 * variables specific to participant so it can be replicated into other entities
946 * and / or moved to the automated test suite
947 */
948 public function testCreateWithCustom() {
949 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
950
951 $params = $this->_params;
952 $params['custom_' . $ids['custom_field_id']] = "custom string";
953
954 $result = $this->callAPIAndDocument($this->_entity, 'create', $params, __FUNCTION__, __FILE__, NULL, 'CreateWithCustomData');
955 $check = $this->callAPISuccess($this->_entity, 'get', [
956 'id' => $result['id'],
957 'contact_id' => $this->_contactID,
958 ]);
959 $this->assertEquals("custom string", $check['values'][$result['id']]['custom_' . $ids['custom_field_id']], ' in line ' . __LINE__);
960 }
961
962 /**
963 * Search on custom field value.
964 */
965 public function testSearchWithCustomDataCRM16036() {
966 // Create a custom field on membership
967 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
968
969 // Create a new membership, but don't assign anything to the custom field.
970 $params = $this->_params;
971 $result = $this->callAPIAndDocument(
972 $this->_entity,
973 'create',
974 $params,
975 __FUNCTION__,
976 __FILE__,
977 NULL,
978 'SearchWithCustomData');
979
980 // search memberships with CRM-16036 as custom field value.
981 // Since we did not touch the custom field of any membership,
982 // this should not return any results.
983 $check = $this->callAPISuccess($this->_entity, 'get', [
984 'custom_' . $ids['custom_field_id'] => "CRM-16036",
985 ]);
986
987 // Cleanup.
988 $this->callAPISuccess($this->_entity, 'delete', [
989 'id' => $result['id'],
990 ]);
991
992 // Assert.
993 $this->assertEquals(0, $check['count']);
994 }
995
996 /**
997 * Test civicrm_contact_memberships_create with membership id (edit
998 * membership).
999 * success expected.
1000 */
1001 public function testMembershipCreateWithId() {
1002 $membershipID = $this->contactMembershipCreate($this->_params);
1003 $params = [
1004 'id' => $membershipID,
1005 'contact_id' => $this->_contactID,
1006 'membership_type_id' => $this->_membershipTypeID,
1007 'join_date' => '2006-01-21',
1008 'start_date' => '2006-01-21',
1009 'end_date' => '2006-12-21',
1010 'source' => 'Payment',
1011 'is_override' => 1,
1012 'status_id' => $this->_membershipStatusID,
1013 ];
1014
1015 $result = $this->callAPISuccess('membership', 'create', $params);
1016
1017 //Update Status and check activities created.
1018 $updateStatus = [
1019 'id' => $result['id'],
1020 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Member_BAO_Membership', 'status_id', 'Cancelled'),
1021 ];
1022 $this->callAPISuccess('Membership', 'create', $updateStatus);
1023 $activities = CRM_Activity_BAO_Activity::getContactActivity($this->_contactID);
1024 $this->assertEquals(2, count($activities));
1025 $activityNames = array_flip(CRM_Utils_Array::collect('activity_name', $activities));
1026 $this->assertArrayHasKey('Membership Signup', $activityNames);
1027 $this->assertArrayHasKey('Change Membership Status', $activityNames);
1028
1029 $this->callAPISuccess('Membership', 'Delete', [
1030 'id' => $result['id'],
1031 ]);
1032 $this->assertEquals($result['id'], $membershipID, "in line " . __LINE__);
1033 }
1034
1035 /**
1036 * Test civicrm_contact_memberships_create with membership id (edit
1037 * membership).
1038 * success expected.
1039 */
1040 public function testMembershipCreateUpdateWithIdNoContact() {
1041 $membershipID = $this->contactMembershipCreate($this->_params);
1042 $params = [
1043 'id' => $membershipID,
1044 'membership_type_id' => $this->_membershipTypeID,
1045 'contact_id' => $this->_contactID,
1046 'join_date' => '2006-01-21',
1047 'start_date' => '2006-01-21',
1048 'end_date' => '2006-12-21',
1049 'source' => 'Payment',
1050 'is_override' => 1,
1051 'status_id' => $this->_membershipStatusID,
1052 ];
1053
1054 $result = $this->callAPISuccess('membership', 'create', $params);
1055 $this->callAPISuccess('Membership', 'Delete', [
1056 'id' => $result['id'],
1057 ]);
1058
1059 $this->assertEquals($result['id'], $membershipID, "in line " . __LINE__);
1060 }
1061
1062 /**
1063 * Test civicrm_contact_memberships_create with membership id (edit
1064 * membership).
1065 * success expected.
1066 */
1067 public function testMembershipCreateUpdateWithIdNoDates() {
1068 $membershipID = $this->contactMembershipCreate($this->_params);
1069 $params = [
1070 'id' => $membershipID,
1071 'contact_id' => $this->_contactID,
1072 'membership_type_id' => $this->_membershipTypeID,
1073 'source' => 'Payment',
1074 'is_override' => 1,
1075 'status_id' => $this->_membershipStatusID,
1076 ];
1077
1078 $result = $this->callAPISuccess('membership', 'create', $params);
1079 $this->callAPISuccess('Membership', 'Delete', [
1080 'id' => $result['id'],
1081 ]);
1082 $this->assertEquals($result['id'], $membershipID, "in line " . __LINE__);
1083 }
1084
1085 /**
1086 * Test civicrm_contact_memberships_create with membership id (edit
1087 * membership).
1088 * success expected.
1089 */
1090 public function testMembershipCreateUpdateWithIdNoDatesNoType() {
1091 $membershipID = $this->contactMembershipCreate($this->_params);
1092 $params = [
1093 'id' => $membershipID,
1094 'source' => 'not much here',
1095 'contact_id' => $this->_contactID,
1096 'is_override' => 1,
1097 'status_id' => $this->_membershipStatusID,
1098 ];
1099
1100 $result = $this->callAPISuccess('membership', 'create', $params);
1101 $this->callAPISuccess('Membership', 'Delete', [
1102 'id' => $result['id'],
1103 ]);
1104 $this->assertEquals($result['id'], $membershipID, "in line " . __LINE__);
1105 }
1106
1107 /**
1108 * Test civicrm_contact_memberships_create with membership id (edit
1109 * membership).
1110 * success expected.
1111 */
1112 public function testMembershipCreateUpdateWithIDAndSource() {
1113 $membershipID = $this->contactMembershipCreate($this->_params);
1114 $params = [
1115 'id' => $membershipID,
1116 'source' => 'changed',
1117 'contact_id' => $this->_contactID,
1118 'status_id' => $this->_membershipStatusID,
1119 'membership_type_id' => $this->_membershipTypeID,
1120 'skipStatusCal' => 1,
1121 ];
1122 $result = $this->callAPISuccess('membership', 'create', $params);
1123 $this->assertEquals($result['id'], $membershipID, "in line " . __LINE__);
1124 $this->callAPISuccess('Membership', 'Delete', [
1125 'id' => $result['id'],
1126 ]);
1127 }
1128
1129 /**
1130 * Change custom field using update.
1131 */
1132 public function testUpdateWithCustom() {
1133 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
1134
1135 $params = $this->_params;
1136 $params['custom_' . $ids['custom_field_id']] = "custom string";
1137 $result = $this->callAPIAndDocument($this->_entity, 'create', $params, __FUNCTION__, __FILE__, NULL, 'UpdateCustomData');
1138 $result = $this->callAPISuccess($this->_entity, 'create', [
1139 'id' => $result['id'],
1140 'custom_' . $ids['custom_field_id'] => "new custom",
1141 ]);
1142 $check = $this->callAPISuccess($this->_entity, 'get', [
1143 'id' => $result['id'],
1144 'contact_id' => $this->_contactID,
1145 ]);
1146
1147 $this->assertEquals("new custom", $check['values'][$result['id']]['custom_' . $ids['custom_field_id']], ' in line ' . __LINE__);
1148 $this->callAPISuccess('Membership', 'Delete', [
1149 'id' => $check['id'],
1150 ]);
1151
1152 $this->customFieldDelete($ids['custom_field_id']);
1153 $this->customGroupDelete($ids['custom_group_id']);
1154 }
1155
1156 /**
1157 * per CRM-15746 check that the id can be altered in an update hook
1158 */
1159 public function testMembershipUpdateCreateHookCRM15746() {
1160 $this->hookClass->setHook('civicrm_pre', [$this, 'hook_civicrm_pre_update_create_membership']);
1161 $result = $this->callAPISuccess('membership', 'create', $this->_params);
1162 $this->callAPISuccess('membership', 'create', ['id' => $result['id'], 'end_date' => '1 year ago']);
1163 $this->callAPISuccessGetCount('membership', [], 2);
1164 $this->hookClass->reset();
1165 $this->callAPISuccess('membership', 'create', ['id' => $result['id'], 'end_date' => '1 year ago']);
1166 $this->callAPISuccessGetCount('membership', [], 2);
1167 }
1168
1169 /**
1170 * Custom hook for update membership.
1171 *
1172 * @param string $op
1173 * @param object $objectName
1174 * @param int $id
1175 * @param array $params
1176 *
1177 * @throws \Exception
1178 */
1179 public function hook_civicrm_pre_update_create_membership($op, $objectName, $id, &$params) {
1180 if ($objectName === 'Membership' && $op === 'edit') {
1181 $existingMembership = $this->callAPISuccessGetSingle('membership', ['id' => $params['id']]);
1182 unset($params['id'], $params['membership_id']);
1183 $params['join_date'] = $params['membership_start_date'] = $params['start_date'] = date('Ymd000000', strtotime($existingMembership['start_date']));
1184 $params = array_merge($existingMembership, $params);
1185 $params['id'] = NULL;
1186 }
1187 }
1188
1189 /**
1190 * Test civicrm_contact_memberships_create Invalid membership data.
1191 * Error expected.
1192 */
1193 public function testMembershipCreateInvalidMemData() {
1194 //membership_contact_id as string
1195 $params = [
1196 'membership_contact_id' => 'Invalid',
1197 'membership_type_id' => $this->_membershipTypeID,
1198 'join_date' => '2011-01-21',
1199 'start_date' => '2010-01-21',
1200 'end_date' => '2008-12-21',
1201 'source' => 'Payment',
1202 'is_override' => 1,
1203 'status_id' => $this->_membershipStatusID,
1204 ];
1205
1206 $this->callAPIFailure('membership', 'create', $params);
1207
1208 //membership_contact_id which is no in contact table
1209 $params['membership_contact_id'] = 999;
1210 $this->callAPIFailure('membership', 'create', $params);
1211
1212 //invalid join date
1213 unset($params['membership_contact_id']);
1214 $params['join_date'] = "invalid";
1215 $this->callAPIFailure('Membership', 'Create', $params);
1216 }
1217
1218 /**
1219 * Test civicrm_contact_memberships_create with membership_contact_id
1220 * membership).
1221 * Success expected.
1222 */
1223 public function testMembershipCreateWithMemContact() {
1224 $params = [
1225 'membership_contact_id' => $this->_contactID,
1226 'membership_type_id' => $this->_membershipTypeID,
1227 'join_date' => '2011-01-21',
1228 'start_date' => '2010-01-21',
1229 'end_date' => '2008-12-21',
1230 'source' => 'Payment',
1231 'is_override' => 1,
1232 'status_id' => $this->_membershipStatusID,
1233 ];
1234
1235 $result = $this->callAPISuccess('membership', 'create', $params);
1236
1237 $this->callAPISuccess('Membership', 'Delete', [
1238 'id' => $result['id'],
1239 ]);
1240 }
1241
1242 /**
1243 * Test civicrm_contact_memberships_create with membership_contact_id
1244 * membership).
1245 * Success expected.
1246 */
1247 public function testMembershipCreateValidMembershipTypeString() {
1248 $params = [
1249 'membership_contact_id' => $this->_contactID,
1250 'membership_type_id' => 'General',
1251 'join_date' => '2011-01-21',
1252 'start_date' => '2010-01-21',
1253 'end_date' => '2008-12-21',
1254 'source' => 'Payment',
1255 'is_override' => 1,
1256 'status_id' => $this->_membershipStatusID,
1257 ];
1258
1259 $result = $this->callAPISuccess('membership', 'create', $params);
1260 $this->assertEquals($this->_membershipTypeID, $result['values'][$result['id']]['membership_type_id']);
1261 $this->callAPISuccess('Membership', 'Delete', [
1262 'id' => $result['id'],
1263 ]);
1264 }
1265
1266 /**
1267 * Test civicrm_contact_memberships_create with membership_contact_id
1268 * membership).
1269 * Success expected.
1270 */
1271 public function testMembershipCreateInValidMembershipTypeString() {
1272 $params = [
1273 'membership_contact_id' => $this->_contactID,
1274 'membership_type_id' => 'invalid',
1275 'join_date' => '2011-01-21',
1276 'start_date' => '2010-01-21',
1277 'end_date' => '2008-12-21',
1278 'source' => 'Payment',
1279 'is_override' => 1,
1280 'status_id' => $this->_membershipStatusID,
1281 ];
1282
1283 $this->callAPIFailure('membership', 'create', $params);
1284 }
1285
1286 /**
1287 * Test that if membership join date is not set it defaults to today.
1288 */
1289 public function testEmptyJoinDate() {
1290 unset($this->_params['join_date'], $this->_params['is_override']);
1291 $result = $this->callAPISuccess($this->_entity, 'create', $this->_params);
1292 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1293 $this->assertEquals(date('Y-m-d', strtotime('now')), $result['join_date']);
1294 $this->assertEquals('2009-01-21', $result['start_date']);
1295 $this->assertEquals('2009-12-21', $result['end_date']);
1296 }
1297
1298 /**
1299 * Test that if membership start date is not set it defaults to correct end date.
1300 * - fixed
1301 */
1302 public function testEmptyStartDateFixed() {
1303 unset($this->_params['start_date'], $this->_params['is_override']);
1304 $this->_params['membership_type_id'] = $this->_membershipTypeID2;
1305 $result = $this->callAPISuccess($this->_entity, 'create', $this->_params);
1306 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1307 $this->assertEquals('2009-01-21', $result['join_date']);
1308 $this->assertEquals('2008-03-01', $result['start_date']);
1309 $this->assertEquals('2009-12-21', $result['end_date']);
1310 }
1311
1312 /**
1313 * Test that if membership start date is not set it defaults to correct end date
1314 * - fixed
1315 */
1316 public function testEmptyStartEndDateFixedOneYear() {
1317 unset($this->_params['start_date'], $this->_params['is_override'], $this->_params['end_date']);
1318 $this->callAPISuccess('membership_type', 'create', ['id' => $this->_membershipTypeID2, 'duration_interval' => 1]);
1319 $this->_params['membership_type_id'] = $this->_membershipTypeID2;
1320 $result = $this->callAPISuccess($this->_entity, 'create', $this->_params);
1321 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1322 $this->assertEquals('2009-01-21', $result['join_date']);
1323 $this->assertEquals('2008-03-01', $result['start_date']);
1324 $this->assertEquals('2010-02-28', $result['end_date']);
1325 }
1326
1327 /**
1328 * Test that if membership start date is not set it defaults to correct end date for fixed multi year memberships.
1329 */
1330 public function testEmptyStartEndDateFixedMultiYear() {
1331 unset($this->_params['start_date'], $this->_params['is_override'], $this->_params['end_date']);
1332 $this->callAPISuccess('membership_type', 'create', ['id' => $this->_membershipTypeID2, 'duration_interval' => 5]);
1333 $this->_params['membership_type_id'] = $this->_membershipTypeID2;
1334 $result = $this->callAPISuccess($this->_entity, 'create', $this->_params);
1335 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1336 $this->assertEquals('2009-01-21', $result['join_date']);
1337 $this->assertEquals('2008-03-01', $result['start_date']);
1338 $this->assertEquals('2014-02-28', $result['end_date']);
1339 }
1340
1341 /**
1342 * CRM-18503 - Test membership join date is correctly set for fixed memberships.
1343 *
1344 * @throws \CRM_Core_Exception
1345 */
1346 public function testMembershipJoinDateFixed() {
1347 $memStatus = CRM_Member_PseudoConstant::membershipStatus();
1348 // Update the fixed membership type to 1 year duration.
1349 $this->callAPISuccess('membership_type', 'create', ['id' => $this->_membershipTypeID2, 'duration_interval' => 1]);
1350 $contactId = $this->createLoggedInUser();
1351 // Create membership with 'Pending' status.
1352 $params = [
1353 'contact_id' => $contactId,
1354 'membership_type_id' => $this->_membershipTypeID2,
1355 'source' => 'test membership',
1356 'is_pay_later' => 0,
1357 'status_id' => array_search('Pending', $memStatus),
1358 'skipStatusCal' => 1,
1359 'is_for_organization' => 1,
1360 ];
1361 $membership = CRM_Member_BAO_Membership::create($params);
1362
1363 // Update membership to 'Completed' and check the dates.
1364 $memParams = [
1365 'id' => $membership->id,
1366 'contact_id' => $contactId,
1367 'is_test' => 0,
1368 'membership_type_id' => $this->_membershipTypeID2,
1369 'num_terms' => 1,
1370 'status_id' => array_search('New', $memStatus),
1371 ];
1372 $result = $this->callAPISuccess('Membership', 'create', $memParams);
1373
1374 // Extend duration interval if join_date exceeds the rollover period.
1375 $joinDate = date('Y-m-d');
1376 $year = date('Y');
1377 $startDate = date('Y-m-d', strtotime(date('Y-03-01')));
1378 $rollOver = TRUE;
1379 if (strtotime($startDate) > time()) {
1380 $rollOver = FALSE;
1381 $startDate = date('Y-m-d', strtotime(date('Y-03-01') . '- 1 year'));
1382 }
1383 $membershipTypeDetails = CRM_Member_BAO_MembershipType::getMembershipTypeDetails($this->_membershipTypeID2);
1384 $fixedPeriodRollover = CRM_Member_BAO_MembershipType::isDuringFixedAnnualRolloverPeriod($joinDate, $membershipTypeDetails, $year, $startDate);
1385 $y = 1;
1386 if ($fixedPeriodRollover && $rollOver) {
1387 $y += 1;
1388 }
1389
1390 $expectedDates = [
1391 'join_date' => date('Ymd'),
1392 'start_date' => str_replace('-', '', $startDate),
1393 'end_date' => date('Ymd', strtotime(date('Y-03-01') . "+ {$y} year - 1 day")),
1394 ];
1395 foreach ($result['values'] as $values) {
1396 foreach ($expectedDates as $date => $val) {
1397 $this->assertEquals($val, $values[$date], "Failed asserting {$date} values");
1398 }
1399 }
1400 }
1401
1402 /**
1403 * Test correct end and start dates are calculated for fixed multi year memberships.
1404 *
1405 * The empty start date is calculated to be the start_date (1 Jan prior to the join_date - so 1 Jan 15)
1406 *
1407 * In this set our start date is after the start day and before the rollover day so we don't get an extra year
1408 * and we end one day before the rollover day. Start day is 1 Jan so we end on 31 Dec
1409 * and we add on 4 years rather than 5 because we are not after the rollover day - so we calculate 31 Dec 2019
1410 */
1411 public function testFixedMultiYearDateSetTwoEmptyStartEndDate() {
1412 unset($this->_params['start_date'], $this->_params['is_override'], $this->_params['end_date']);
1413
1414 $this->callAPISuccess('membership_type', 'create', [
1415 'id' => $this->_membershipTypeID2,
1416 'duration_interval' => 5,
1417 // Ie 1 Jan.
1418 'fixed_period_start_day' => '101',
1419 // Ie. 1 Nov.
1420 'fixed_period_rollover_day' => '1101',
1421 ]);
1422 $this->_params['membership_type_id'] = $this->_membershipTypeID2;
1423 $dates = [
1424 'join_date' => '28-Jan 2015',
1425 ];
1426 $result = $this->callAPISuccess($this->_entity, 'create', array_merge($this->_params, $dates));
1427 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1428 $this->assertEquals('2015-01-28', $result['join_date']);
1429 $this->assertEquals('2015-01-01', $result['start_date']);
1430 $this->assertEquals('2019-12-31', $result['end_date']);
1431 }
1432
1433 /**
1434 * Test that correct end date is calculated for fixed multi year memberships and start date is not changed.
1435 *
1436 * In this set our start date is after the start day and before the rollover day so we don't get an extra year
1437 * and we end one day before the rollover day. Start day is 1 Jan so we end on 31 Dec
1438 * and we add on 4 years rather than 5 because we are not after the rollover day - so we calculate 31 Dec 2019
1439 */
1440 public function testFixedMultiYearDateSetTwoEmptyEndDate() {
1441 unset($this->_params['start_date'], $this->_params['is_override'], $this->_params['end_date']);
1442
1443 $this->callAPISuccess('membership_type', 'create', [
1444 'id' => $this->_membershipTypeID2,
1445 'duration_interval' => 5,
1446 // Ie 1 Jan.
1447 'fixed_period_start_day' => '101',
1448 // Ie. 1 Nov.
1449 'fixed_period_rollover_day' => '1101',
1450 ]);
1451 $this->_params['membership_type_id'] = $this->_membershipTypeID2;
1452 $dates = [
1453 'start_date' => '28-Jan 2015',
1454 'join_date' => '28-Jan 2015',
1455 ];
1456 $result = $this->callAPISuccess($this->_entity, 'create', array_merge($this->_params, $dates));
1457 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1458 $this->assertEquals('2015-01-28', $result['join_date']);
1459 $this->assertEquals('2015-01-28', $result['start_date']);
1460 $this->assertEquals('2019-12-31', $result['end_date']);
1461 }
1462
1463 /**
1464 * Test correct end and start dates are calculated for fixed multi year memberships.
1465 *
1466 * The empty start date is calculated to be the start_date (1 Jan prior to the join_date - so 1 Jan 15)
1467 *
1468 * In this set our start date is after the start day and before the rollover day so we don't get an extra year
1469 * and we end one day before the rollover day. Start day is 1 Jan so we end on 31 Dec
1470 * and we add on <1 years rather than > 1 because we are not after the rollover day - so we calculate 31 Dec 2015
1471 */
1472 public function testFixedSingleYearDateSetTwoEmptyStartEndDate() {
1473 unset($this->_params['start_date'], $this->_params['is_override'], $this->_params['end_date']);
1474
1475 $this->callAPISuccess('membership_type', 'create', [
1476 'id' => $this->_membershipTypeID2,
1477 'duration_interval' => 1,
1478 // Ie 1 Jan.
1479 'fixed_period_start_day' => '101',
1480 // Ie. 1 Nov.
1481 'fixed_period_rollover_day' => '1101',
1482 ]);
1483 $this->_params['membership_type_id'] = $this->_membershipTypeID2;
1484 $dates = [
1485 'join_date' => '28-Jan 2015',
1486 ];
1487 $result = $this->callAPISuccess($this->_entity, 'create', array_merge($this->_params, $dates));
1488 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1489 $this->assertEquals('2015-01-28', $result['join_date']);
1490 $this->assertEquals('2015-01-01', $result['start_date']);
1491 $this->assertEquals('2015-12-31', $result['end_date']);
1492 }
1493
1494 /**
1495 * Test correct end date for fixed single year memberships is calculated and start_date is not changed.
1496 *
1497 * In this set our start date is after the start day and before the rollover day so we don't get an extra year
1498 * and we end one day before the rollover day. Start day is 1 Jan so we end on 31 Dec
1499 * and we add on <1 years rather than > 1 because we are not after the rollover day - so we calculate 31 Dec 2015
1500 */
1501 public function testFixedSingleYearDateSetTwoEmptyEndDate() {
1502 unset($this->_params['start_date'], $this->_params['is_override'], $this->_params['end_date']);
1503
1504 $this->callAPISuccess('membership_type', 'create', [
1505 'id' => $this->_membershipTypeID2,
1506 'duration_interval' => 1,
1507 // Ie 1 Jan.
1508 'fixed_period_start_day' => '101',
1509 // Ie. 1 Nov.
1510 'fixed_period_rollover_day' => '1101',
1511 ]);
1512 $this->_params['membership_type_id'] = $this->_membershipTypeID2;
1513 $dates = [
1514 'start_date' => '28-Jan 2015',
1515 'join_date' => '28-Jan 2015',
1516 ];
1517 $result = $this->callAPISuccess($this->_entity, 'create', array_merge($this->_params, $dates));
1518 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1519 $this->assertEquals('2015-01-28', $result['join_date']);
1520 $this->assertEquals('2015-01-28', $result['start_date']);
1521 $this->assertEquals('2015-12-31', $result['end_date']);
1522 }
1523
1524 /**
1525 * Test that correct end date is calculated for fixed multi year memberships and start date is not changed.
1526 *
1527 * In this set our start date is after the start day and after the rollover day so we do get an extra year
1528 * and we end one day before the rollover day. Start day is 1 Nov so we end on 31 Oct
1529 * and we add on 1 year we are after the rollover day - so we calculate 31 Oct 2016
1530 */
1531 public function testFixedSingleYearDateSetThreeEmptyEndDate() {
1532 unset($this->_params['start_date'], $this->_params['is_override'], $this->_params['end_date']);
1533
1534 $this->callAPISuccess('membership_type', 'create', [
1535 'id' => $this->_membershipTypeID2,
1536 'duration_interval' => 1,
1537 // Ie. 1 Nov.
1538 'fixed_period_start_day' => '1101',
1539 // Ie 1 Jan.
1540 'fixed_period_rollover_day' => '101',
1541 ]);
1542 $this->_params['membership_type_id'] = $this->_membershipTypeID2;
1543 $dates = [
1544 'start_date' => '28-Jan 2015',
1545 'join_date' => '28-Jan 2015',
1546 ];
1547 $result = $this->callAPISuccess($this->_entity, 'create', array_merge($this->_params, $dates));
1548 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1549 $this->assertEquals('2015-01-28', $result['join_date']);
1550 $this->assertEquals('2015-01-28', $result['start_date']);
1551 $this->assertEquals('2016-10-31', $result['end_date']);
1552 }
1553
1554 /**
1555 * Test correct end and start dates are calculated for fixed multi year memberships.
1556 *
1557 * The empty start date is calculated to be the start_date (1 Nov prior to the join_date - so 1 Nov 14)
1558 *
1559 * In this set our start date is after the start day and after the rollover day so we do get an extra year
1560 * and we end one day before the rollover day. Start day is 1 Nov so we end on 31 Oct
1561 * and we add on 1 year we are after the rollover day - so we calculate 31 Oct 2016
1562 */
1563 public function testFixedSingleYearDateSetThreeEmptyStartEndDate() {
1564 unset($this->_params['start_date'], $this->_params['is_override'], $this->_params['end_date']);
1565
1566 $this->callAPISuccess('membership_type', 'create', [
1567 'id' => $this->_membershipTypeID2,
1568 'duration_interval' => 1,
1569 // Ie. 1 Nov.
1570 'fixed_period_start_day' => '1101',
1571 // Ie 1 Jan.
1572 'fixed_period_rollover_day' => '101',
1573 ]);
1574 $this->_params['membership_type_id'] = $this->_membershipTypeID2;
1575 $dates = [
1576 'join_date' => '28-Jan 2015',
1577 ];
1578 $result = $this->callAPISuccess($this->_entity, 'create', array_merge($this->_params, $dates));
1579 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1580 $this->assertEquals('2015-01-28', $result['join_date']);
1581 $this->assertEquals('2014-11-01', $result['start_date']);
1582 $this->assertEquals('2016-10-31', $result['end_date']);
1583 }
1584
1585 /**
1586 * Test that correct end date is calculated for fixed multi year memberships and start date is not changed.
1587 *
1588 * In this set our start date is after the start day and after the rollover day so we do get an extra year
1589 * and we end one day before the rollover day. Start day is 1 Nov so we end on 31 Oct
1590 * and we add on 5 years we are after the rollover day - so we calculate 31 Oct 2020
1591 */
1592 public function testFixedMultiYearDateSetThreeEmptyEndDate() {
1593 unset($this->_params['start_date'], $this->_params['is_override'], $this->_params['end_date']);
1594
1595 $this->callAPISuccess('membership_type', 'create', [
1596 'id' => $this->_membershipTypeID2,
1597 'duration_interval' => 5,
1598 // Ie. 1 Nov.
1599 'fixed_period_start_day' => '1101',
1600 // Ie 1 Jan.
1601 'fixed_period_rollover_day' => '101',
1602 ]);
1603 $this->_params['membership_type_id'] = $this->_membershipTypeID2;
1604 $dates = [
1605 'start_date' => '28-Jan 2015',
1606 'join_date' => '28-Jan 2015',
1607 ];
1608 $result = $this->callAPISuccess($this->_entity, 'create', array_merge($this->_params, $dates));
1609 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1610 $this->assertEquals('2015-01-28', $result['join_date']);
1611 $this->assertEquals('2015-01-28', $result['start_date']);
1612 $this->assertEquals('2020-10-31', $result['end_date']);
1613 }
1614
1615 /**
1616 * Test correct end and start dates are calculated for fixed multi year memberships.
1617 *
1618 * The empty start date is calculated to be the start_date (1 Nov prior to the join_date - so 1 Nov 14)
1619 *
1620 * The empty start date is calculated to be the start_date (1 Nov prior to the join_date - so 1 Nov 14)
1621 * In this set our join date is after the start day and after the rollover day so we do get an extra year
1622 * and we end one day before the rollover day. Start day is 1 Nov so we end on 31 Oct
1623 * and we add on 5 years we are after the rollover day - so we calculate 31 Oct 2020
1624 */
1625 public function testFixedMultiYearDateSetThreeEmptyStartEndDate() {
1626 unset($this->_params['start_date'], $this->_params['is_override'], $this->_params['end_date']);
1627
1628 $this->callAPISuccess('membership_type', 'create', [
1629 'id' => $this->_membershipTypeID2,
1630 'duration_interval' => 5,
1631 // Ie. 1 Nov.
1632 'fixed_period_start_day' => '1101',
1633 // Ie 1 Jan.
1634 'fixed_period_rollover_day' => '101',
1635 ]);
1636 $this->_params['membership_type_id'] = $this->_membershipTypeID2;
1637 $dates = [
1638 'join_date' => '28-Jan 2015',
1639 ];
1640 $result = $this->callAPISuccess($this->_entity, 'create', array_merge($this->_params, $dates));
1641 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1642 $this->assertEquals('2015-01-28', $result['join_date']);
1643 $this->assertEquals('2014-11-01', $result['start_date']);
1644 $this->assertEquals('2020-10-31', $result['end_date']);
1645 }
1646
1647 /**
1648 * Test that if membership start date is not set it defaults to correct end date for fixed single year memberships.
1649 */
1650 public function testEmptyStartDateRolling() {
1651 unset($this->_params['start_date'], $this->_params['is_override']);
1652 $result = $this->callAPISuccess($this->_entity, 'create', $this->_params);
1653 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1654 $this->assertEquals('2009-01-21', $result['join_date']);
1655 $this->assertEquals('2009-01-21', $result['start_date']);
1656 $this->assertEquals('2009-12-21', $result['end_date']);
1657 }
1658
1659 /**
1660 * Test that if membership end date is not set it defaults to correct end date.
1661 * - rolling
1662 */
1663 public function testEmptyEndDateFixed() {
1664 unset($this->_params['start_date'], $this->_params['is_override'], $this->_params['end_date']);
1665 $this->_params['membership_type_id'] = $this->_membershipTypeID2;
1666 $result = $this->callAPISuccess($this->_entity, 'create', $this->_params);
1667 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1668 $this->assertEquals('2009-01-21', $result['join_date']);
1669 $this->assertEquals('2008-03-01', $result['start_date']);
1670 $this->assertEquals('2010-02-28', $result['end_date']);
1671 }
1672
1673 /**
1674 * Test that if membership end date is not set it defaults to correct end date.
1675 * - rolling
1676 */
1677 public function testEmptyEndDateRolling() {
1678 unset($this->_params['is_override'], $this->_params['end_date']);
1679 $this->_params['membership_type_id'] = $this->_membershipTypeID;
1680 $result = $this->callAPISuccess($this->_entity, 'create', $this->_params);
1681 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1682 $this->assertEquals('2009-01-21', $result['join_date']);
1683 $this->assertEquals('2009-01-21', $result['start_date']);
1684 $this->assertEquals('2010-01-20', $result['end_date']);
1685 }
1686
1687 /**
1688 * Test that if dates are set they not over-ridden if id is passed in
1689 */
1690 public function testMembershipDatesNotOverridden() {
1691 $result = $this->callAPISuccess($this->_entity, 'create', $this->_params);
1692 unset($this->_params['end_date'], $this->_params['start_date']);
1693 $this->_params['id'] = $result['id'];
1694 $this->callAPISuccess($this->_entity, 'create', $this->_params);
1695 $result = $this->callAPISuccess($this->_entity, 'getsingle', ['id' => $result['id']]);
1696 $this->assertEquals('2009-01-21', $result['join_date']);
1697 $this->assertEquals('2009-01-21', $result['start_date']);
1698 $this->assertEquals('2009-12-21', $result['end_date']);
1699
1700 }
1701
1702 /**
1703 * Test that a contribution linked to multiple memberships results in all being updated.
1704 *
1705 * @throws \CRM_Core_Exception
1706 */
1707 public function testMultipleMembershipContribution() {
1708 $this->createMultipleMembershipOrder();
1709 $this->callAPISuccess('Payment', 'create', [
1710 'contribution_id' => $this->ids['Contribution'][0],
1711 'payment_instrument_id' => 'Check',
1712 'total_amount' => 400,
1713 ]);
1714 $memberships = $this->callAPISuccess('membership', 'get')['values'];
1715 $this->assertCount(2, $memberships);
1716 }
1717
1718 /**
1719 * Test that all membership types are returned when getoptions is called.
1720 *
1721 * This test locks in current behaviour where types for all domains are returned. It should possibly be domain
1722 * specific but that should only be done in conjunction with adding a hook to allow that to be altered as the
1723 * multisite use case expects the master domain to be able to see all sites.
1724 *
1725 * See CRM-17075.
1726 */
1727 public function testGetOptionsMembershipTypeID() {
1728 $options = $this->callAPISuccess('Membership', 'getoptions', ['field' => 'membership_type_id']);
1729 $this->assertEquals('Another one', array_pop($options['values']));
1730 $this->assertEquals('General', array_pop($options['values']));
1731 $this->assertEquals(NULL, array_pop($options['values']));
1732 }
1733
1734 }