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