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