Merge pull request #22805 from braders/permission_denied_wordpress_improvement-altern...
[civicrm-core.git] / tests / phpunit / CRM / Member / Form / 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 /**
13 * File for the MembershipTest class
14 *
15 * (PHP 5)
16 *
17 * @author Walt Haas <walt@dharmatech.org> (801) 534-1262
18 */
19
20 use Civi\Api4\FinancialType;
21 use Civi\Api4\MembershipType;
22
23 /**
24 * Test CRM_Member_Form_Membership functions.
25 *
26 * @package CiviCRM
27 * @group headless
28 */
29 class CRM_Member_Form_MembershipTest extends CiviUnitTestCase {
30
31 use CRMTraits_Financial_OrderTrait;
32 use CRMTraits_Financial_PriceSetTrait;
33
34 /**
35 * @var int
36 */
37 protected $_individualId;
38 protected $_contribution;
39 protected $_financialTypeId = 1;
40 protected $_entity = 'Membership';
41 protected $_params;
42 protected $_ids = [];
43 protected $_paymentProcessorID;
44
45 /**
46 * Membership type ID for annual fixed membership.
47 *
48 * @var int
49 */
50 protected $membershipTypeAnnualFixedID;
51
52 /**
53 * Parameters to create payment processor.
54 *
55 * @var array
56 */
57 protected $_processorParams = [];
58
59 /**
60 * ID of created membership.
61 *
62 * @var int
63 */
64 protected $_membershipID;
65
66 /**
67 * Payment instrument mapping.
68 *
69 * @var array
70 */
71 protected $paymentInstruments = [];
72
73 /**
74 * @var CiviMailUtils
75 */
76 protected $mut;
77
78 /**
79 * Test setup for every test.
80 *
81 * Connect to the database, truncate the tables that will be used
82 * and redirect stdin to a temporary file.
83 */
84 public function setUp(): void {
85 $this->_apiversion = 3;
86 parent::setUp();
87
88 $this->_individualId = $this->individualCreate();
89 $this->_paymentProcessorID = $this->processorCreate();
90
91 $this->ids['contact']['organization'] = $this->organizationCreate();
92 $this->ids['contact']['organization2'] = $this->organizationCreate();
93 $this->ids['relationship_type']['member'] = $this->callAPISuccess('RelationshipType', 'create', [
94 'name_a_b' => 'Member of',
95 'label_a_b' => 'Member of',
96 'name_b_a' => 'Member is',
97 'label_b_a' => 'Member is',
98 'contact_type_a' => 'Individual',
99 'contact_type_b' => 'Organization',
100 ])['id'];
101 $this->ids['membership_type']['AnnualFixed'] = $this->callAPISuccess('MembershipType', 'create', [
102 'domain_id' => 1,
103 'name' => 'AnnualFixed',
104 'member_of_contact_id' => $this->ids['contact']['organization'],
105 'duration_unit' => 'year',
106 'minimum_fee' => 50,
107 'duration_interval' => 1,
108 'period_type' => 'fixed',
109 'fixed_period_start_day' => '101',
110 'fixed_period_rollover_day' => '1231',
111 'relationship_type_id' => [$this->ids['relationship_type']['member']],
112 'relationship_direction' => ['b_a'],
113 'financial_type_id' => 2,
114 ])['id'];
115
116 $this->ids['membership_type']['AnnualRolling'] = $this->callAPISuccess('MembershipType', 'create', [
117 'name' => 'AnnualRolling',
118 'member_of_contact_id' => $this->ids['contact']['organization'],
119 'duration_unit' => 'year',
120 'duration_interval' => 1,
121 'period_type' => 'rolling',
122 'relationship_type_id' => [$this->ids['relationship_type']['member']],
123 'relationship_direction' => ['b_a'],
124 'financial_type_id' => 'Member Dues',
125 ])['id'];
126
127 $this->ids['membership_type']['AnnualRollingOrg2'] = $this->callAPISuccess('MembershipType', 'create', [
128 'name' => 'AnnualRolling1',
129 'member_of_contact_id' => $this->ids['contact']['organization2'],
130 'duration_unit' => 'year',
131 'duration_interval' => 1,
132 'period_type' => 'rolling',
133 'relationship_type_id' => [$this->ids['relationship_type']['member']],
134 'relationship_direction' => ['b_a'],
135 'financial_type_id' => 'Member Dues',
136 ])['id'];
137
138 $this->ids['membership_type']['lifetime'] = $this->callAPISuccess('MembershipType', 'create', [
139 'name' => 'Lifetime',
140 'member_of_contact_id' => $this->ids['contact']['organization'],
141 'duration_unit' => 'lifetime',
142 'duration_interval' => 1,
143 'relationship_type_id' => $this->ids['relationship_type']['member'],
144 'relationship_direction' => 'b_a',
145 'financial_type_id' => 'Member Dues',
146 'period_type' => 'rolling',
147 ])['id'];
148
149 $instruments = $this->callAPISuccess('Contribution', 'getoptions', ['field' => 'payment_instrument_id']);
150 $this->paymentInstruments = $instruments['values'];
151 }
152
153 /**
154 * Clean up after each test.
155 */
156 public function tearDown(): void {
157 $this->quickCleanUpFinancialEntities();
158 $this->quickCleanup(
159 [
160 'civicrm_relationship',
161 'civicrm_membership_type',
162 'civicrm_membership',
163 'civicrm_uf_match',
164 'civicrm_email',
165 ]
166 );
167 $this->callAPISuccess('Contact', 'delete', ['id' => $this->ids['contact']['organization'], 'skip_undelete' => TRUE]);
168 $this->callAPISuccess('RelationshipType', 'delete', ['id' => $this->ids['relationship_type']['member']]);
169 parent::tearDown();
170 }
171
172 /**
173 * Test CRM_Member_Form_Membership::formRule() with a parameter
174 * that has an empty contact_select_id value
175 *
176 * @throws \CiviCRM_API3_Exception
177 * @throws \CRM_Core_Exception
178 */
179 public function testFormRuleEmptyContact(): void {
180 $params = [
181 'contact_select_id' => 0,
182 'membership_type_id' => [1 => NULL],
183 ];
184 $files = [];
185 $obj = new CRM_Member_Form_Membership();
186 $rc = CRM_Member_Form_Membership::formRule($params, $files, $obj);
187 $this->assertIsArray($rc);
188 $this->assertArrayHasKey('membership_type_id', $rc);
189
190 $params['membership_type_id'] = [1 => 3];
191 $rc = CRM_Member_Form_Membership::formRule($params, $files, $obj);
192 $this->assertIsArray($rc);
193 $this->assertArrayHasKey('join_date', $rc);
194 }
195
196 /**
197 * Test that form rule fails if start date is before join date.
198 *
199 * Test CRM_Member_Form_Membership::formRule() with a parameter
200 * that has an start date before the join date and a rolling
201 * membership type.
202 */
203 public function testFormRuleRollingEarlyStart(): void {
204 $unixNow = time();
205 $unixYesterday = $unixNow - (24 * 60 * 60);
206 $ymdYesterday = date('Y-m-d', $unixYesterday);
207 $params = [
208 'join_date' => date('Y-m-d'),
209 'start_date' => $ymdYesterday,
210 'end_date' => '',
211 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']],
212 ];
213 $files = [];
214 $obj = new CRM_Member_Form_Membership();
215 $rc = CRM_Member_Form_Membership::formRule($params, $files, $obj);
216 $this->assertisArray($rc);
217 $this->assertArrayHasKey('start_date', $rc);
218 }
219
220 /**
221 * Test CRM_Member_Form_Membership::formRule() with a parameter
222 * that has an end date before the start date and a rolling
223 * membership type
224 */
225 public function testFormRuleRollingEarlyEnd() {
226 $unixNow = time();
227 $unixYesterday = $unixNow - (24 * 60 * 60);
228 $ymdYesterday = date('Y-m-d', $unixYesterday);
229 $params = [
230 'join_date' => date('Y-m-d'),
231 'start_date' => date('Y-m-d'),
232 'end_date' => $ymdYesterday,
233 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']],
234 ];
235 $files = [];
236 $obj = new CRM_Member_Form_Membership();
237 $rc = CRM_Member_Form_Membership::formRule($params, $files, $obj);
238 $this->assertIsArray($rc);
239 $this->assertTrue(array_key_exists('end_date', $rc));
240 }
241
242 /**
243 * Test CRM_Member_Form_Membership::formRule() with end date but no start date and a rolling membership type.
244 */
245 public function testFormRuleRollingEndNoStart() {
246 $unixNow = time();
247 $unixYearFromNow = $unixNow + (365 * 24 * 60 * 60);
248 $ymdYearFromNow = date('Y-m-d', $unixYearFromNow);
249 $params = [
250 'join_date' => date('Y-m-d'),
251 'start_date' => '',
252 'end_date' => $ymdYearFromNow,
253 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']],
254 ];
255 $files = [];
256 $obj = new CRM_Member_Form_Membership();
257 $rc = $obj::formRule($params, $files, $obj);
258 $this->assertIsArray($rc);
259 $this->assertTrue(array_key_exists('start_date', $rc));
260 }
261
262 /**
263 * Test CRM_Member_Form_Membership::formRule() with a parameter
264 * that has an end date and a lifetime membership type
265 */
266 public function testFormRuleRollingLifetimeEnd() {
267 $unixNow = time();
268 $unixYearFromNow = $unixNow + (365 * 24 * 60 * 60);
269 $params = [
270 'join_date' => date('Y-m-d'),
271 'start_date' => date('Y-m-d'),
272 'end_date' => date('Y-m-d',
273 $unixYearFromNow
274 ),
275 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['lifetime']],
276 ];
277 $files = [];
278 $obj = new CRM_Member_Form_Membership();
279 $rc = $obj::formRule($params, $files, $obj);
280 $this->assertIsArray($rc);
281 $this->assertTrue(array_key_exists('status_id', $rc));
282 }
283
284 /**
285 * Test CRM_Member_Form_Membership::formRule() with a parameter
286 * that has permanent override and no status
287 *
288 * @throws \CiviCRM_API3_Exception
289 * @throws \CRM_Core_Exception
290 */
291 public function testFormRulePermanentOverrideWithNoStatus() {
292 $params = [
293 'join_date' => date('Y-m-d'),
294 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualFixed']],
295 'is_override' => TRUE,
296 ];
297 $files = [];
298 $obj = new CRM_Member_Form_Membership();
299 $rc = $obj::formRule($params, $files, $obj);
300 $this->assertIsArray($rc);
301 $this->assertTrue(array_key_exists('status_id', $rc));
302 }
303
304 public function testFormRuleUntilDateOverrideWithValidOverrideEndDate() {
305 $params = [
306 'join_date' => date('Y-m-d'),
307 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualFixed']],
308 'is_override' => TRUE,
309 'status_id' => 1,
310 'status_override_end_date' => date('Y-m-d'),
311 ];
312 $files = [];
313 $membershipForm = new CRM_Member_Form_Membership();
314 $validationResponse = CRM_Member_Form_Membership::formRule($params, $files, $membershipForm);
315 $this->assertTrue($validationResponse);
316 }
317
318 public function testFormRuleUntilDateOverrideWithNoOverrideEndDate() {
319 $params = [
320 'join_date' => date('Y-m-d'),
321 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualFixed']],
322 'is_override' => CRM_Member_StatusOverrideTypes::UNTIL_DATE,
323 'status_id' => 1,
324 ];
325 $files = [];
326 $membershipForm = new CRM_Member_Form_Membership();
327 $validationResponse = CRM_Member_Form_Membership::formRule($params, $files, $membershipForm);
328 $this->assertIsArray($validationResponse);
329 $this->assertEquals('Please enter the Membership override end date.', $validationResponse['status_override_end_date']);
330 }
331
332 /**
333 * Test CRM_Member_Form_Membership::formRule() with a join date
334 * of one month from now and a rolling membership type
335 */
336 public function testFormRuleRollingJoin1MonthFromNow() {
337 $unixNow = time();
338 $unix1MFmNow = $unixNow + (31 * 24 * 60 * 60);
339 $params = [
340 'join_date' => date('Y-m-d', $unix1MFmNow),
341 'start_date' => '',
342 'end_date' => '',
343 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']],
344 ];
345 $files = [];
346 $obj = new CRM_Member_Form_Membership();
347 $rc = $obj::formRule($params, $files, $obj);
348
349 // Should have found no valid membership status.
350 $this->assertIsArray($rc);
351 $this->assertTrue(array_key_exists('_qf_default', $rc));
352 }
353
354 /**
355 * Test CRM_Member_Form_Membership::formRule() with a join date of today and a rolling membership type.
356 */
357 public function testFormRuleRollingJoinToday() {
358 $params = [
359 'join_date' => date('Y-m-d'),
360 'start_date' => '',
361 'end_date' => '',
362 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']],
363 ];
364 $files = [];
365 $obj = new CRM_Member_Form_Membership();
366 $rc = $obj::formRule($params, $files, $obj);
367
368 // Should have found New membership status
369 $this->assertTrue($rc);
370 }
371
372 /**
373 * Test CRM_Member_Form_Membership::formRule() with a join date
374 * of one month ago and a rolling membership type
375 */
376 public function testFormRuleRollingJoin1MonthAgo() {
377 $unixNow = time();
378 $unix1MAgo = $unixNow - (31 * 24 * 60 * 60);
379 $params = [
380 'join_date' => date('Y-m-d', $unix1MAgo),
381 'start_date' => '',
382 'end_date' => '',
383 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']],
384 ];
385 $files = [];
386 $obj = new CRM_Member_Form_Membership();
387 $rc = $obj::formRule($params, $files, $obj);
388
389 // Should have found New membership status.
390 $this->assertTrue($rc);
391 }
392
393 /**
394 * Test CRM_Member_Form_Membership::formRule() with a join date of six months ago and a rolling membership type.
395 */
396 public function testFormRuleRollingJoin6MonthsAgo() {
397 $unixNow = time();
398 $unix6MAgo = $unixNow - (180 * 24 * 60 * 60);
399 $params = [
400 'join_date' => date('Y-m-d', $unix6MAgo),
401 'start_date' => '',
402 'end_date' => '',
403 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']],
404 ];
405 $files = [];
406 $obj = new CRM_Member_Form_Membership();
407 $rc = $obj::formRule($params, $files, $obj);
408
409 // Should have found Current membership status.
410 $this->assertTrue($rc);
411 }
412
413 /**
414 * Test CRM_Member_Form_Membership::formRule() with a join date
415 * of one year+ ago and a rolling membership type
416 */
417 public function testFormRuleRollingJoin1YearAgo() {
418 $unixNow = time();
419 $unix1YAgo = $unixNow - (370 * 24 * 60 * 60);
420 $params = [
421 'join_date' => date('Y-m-d', $unix1YAgo),
422 'start_date' => '',
423 'end_date' => '',
424 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']],
425 ];
426 $files = [];
427 $obj = new CRM_Member_Form_Membership();
428 $rc = $obj::formRule($params, $files, $obj);
429
430 // Should have found Grace membership status
431 $this->assertTrue($rc);
432 }
433
434 /**
435 * Test CRM_Member_Form_Membership::formRule() with a join date
436 * of two years ago and a rolling membership type
437 */
438 public function testFormRuleRollingJoin2YearsAgo() {
439 $unixNow = time();
440 $unix2YAgo = $unixNow - (2 * 365 * 24 * 60 * 60);
441 $params = [
442 'join_date' => date('Y-m-d', $unix2YAgo),
443 'start_date' => '',
444 'end_date' => '',
445 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']],
446 ];
447 $files = [];
448 $obj = new CRM_Member_Form_Membership();
449 $rc = $obj::formRule($params, $files, $obj);
450
451 // Should have found Expired membership status
452 $this->assertTrue($rc);
453 }
454
455 /**
456 * Test CRM_Member_Form_Membership::formRule() with a current status.
457 *
458 * The setup is a join date of six months ago and a fixed membership type.
459 */
460 public function testFormRuleFixedJoin6MonthsAgo() {
461 $unixNow = time();
462 $unix6MAgo = $unixNow - (180 * 24 * 60 * 60);
463 $params = [
464 'join_date' => date('Y-m-d', $unix6MAgo),
465 'start_date' => '',
466 'end_date' => '',
467 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualFixed']],
468 ];
469 $files = [];
470 $obj = new CRM_Member_Form_Membership();
471 $rc = $obj::formRule($params, $files, $obj);
472
473 // Should have found Current membership status
474 $this->assertTrue($rc);
475 }
476
477 /**
478 * Test the submit function of the membership form.
479 *
480 * @param string $thousandSeparator
481 *
482 * @throws \CRM_Core_Exception
483 * @throws \CiviCRM_API3_Exception
484 *
485 * @dataProvider getThousandSeparators
486 */
487 public function testSubmit(string $thousandSeparator): void {
488 CRM_Core_Session::singleton()->getStatus(TRUE);
489 $this->setCurrencySeparators($thousandSeparator);
490 $form = $this->getForm();
491 $this->mut = new CiviMailUtils($this, TRUE);
492 $form->_mode = 'test';
493 $this->createLoggedInUser();
494 $params = [
495 'cid' => $this->_individualId,
496 'contact_id' => $this->_individualId,
497 'join_date' => date('Y-m-d'),
498 // This format reflects the organisation & then the type.
499 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualFixed']],
500 'auto_renew' => '0',
501 'max_related' => '',
502 'num_terms' => 2,
503 'source' => '',
504 'total_amount' => $this->formatMoneyInput(1234.56),
505 //Member dues, see data.xml
506 'financial_type_id' => '2',
507 'soft_credit_type_id' => '',
508 'soft_credit_contact_id' => '',
509 'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
510 'payment_processor_id' => $this->_paymentProcessorID,
511 'credit_card_number' => '4111111111111111',
512 'cvv2' => '123',
513 'credit_card_exp_date' => [
514 'M' => '9',
515 'Y' => date('Y', strtotime('+ 2 years')),
516 ],
517 'credit_card_type' => 'Visa',
518 'billing_first_name' => 'Test',
519 'billing_last_name' => 'Last',
520 'billing_street_address-5' => '10 Test St',
521 'billing_city-5' => 'Test',
522 'billing_state_province_id-5' => '1003',
523 'billing_postal_code-5' => '90210',
524 'billing_country_id-5' => '1228',
525 'send_receipt' => TRUE,
526 'receipt_text' => 'Receipt text',
527 ];
528 $form->_contactID = $this->_individualId;
529 $form->testSubmit($params);
530 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
531 $membershipEndYear = date('Y') + 1;
532 if (date('m-d') == '12-31') {
533 // If you join on Dec 31, then the first term would end right away, so
534 // add a year.
535 $membershipEndYear++;
536 }
537 $this->assertEquals($membershipEndYear . '-12-31', $membership['end_date']);
538 $this->callAPISuccessGetCount('ContributionRecur', ['contact_id' => $this->_individualId], 0);
539 $contribution = $this->callAPISuccess('Contribution', 'get', [
540 'contact_id' => $this->_individualId,
541 'is_test' => TRUE,
542 ]);
543
544 //CRM-20264 : Check that CC type and number (last 4 digit) is stored during backoffice membership payment
545 $financialTrxn = $this->callAPISuccessGetSingle(
546 'Payment',
547 [
548 'contribution_id' => $contribution['id'],
549 'return' => ['card_type_id', 'pan_truncation'],
550 ]
551 );
552 $this->assertEquals(1, $financialTrxn['card_type_id']);
553 $this->assertEquals(1111, $financialTrxn['pan_truncation']);
554
555 $this->callAPISuccessGetCount('LineItem', [
556 'entity_id' => $membership['id'],
557 'entity_table' => 'civicrm_membership',
558 'contribution_id' => $contribution['id'],
559 ], 1);
560
561 $this->_checkFinancialRecords([
562 'id' => $contribution['id'],
563 'total_amount' => 1234.56,
564 'financial_account_id' => 2,
565 'payment_instrument_id' => $this->callAPISuccessGetValue('PaymentProcessor', [
566 'id' => $this->_paymentProcessorID,
567 'return' => 'payment_instrument_id',
568 ]),
569 ], 'online');
570 $this->mut->checkMailLog([
571 Civi::format()->money('1234.56'),
572 'Receipt text',
573 ]);
574 $this->mut->stop();
575 $this->assertEquals([
576 [
577 'text' => 'AnnualFixed membership for Mr. Anthony Anderson II has been added. The new membership End Date is December 31st, ' . $membershipEndYear . '. A membership confirmation and receipt has been sent to anthony_anderson@civicrm.org.',
578 'title' => 'Complete',
579 'type' => 'success',
580 'options' => NULL,
581 ],
582 ], CRM_Core_Session::singleton()->getStatus());
583 }
584
585 /**
586 * Test the submit function of the membership form for unpaid membership.
587 *
588 * It turns out that no receipt is sent. This just locks in that pre-existing
589 * behaviour.
590 *
591 * @throws \CRM_Core_Exception
592 * @throws \CiviCRM_API3_Exception
593 * @throws \API_Exception
594 */
595 public function testSubmitUnpaid(): void {
596 $this->mut = new CiviMailUtils($this, TRUE);
597 $this->createLoggedInUser();
598 MembershipType::update()->addWhere('id', '=', $this->ids['membership_type']['AnnualFixed'])
599 ->setValues(['minimum_fee' => 0])->execute();
600 $form = $this->getForm([
601 'contact_id' => $this->_individualId,
602 'join_date' => date('Y-m-d'),
603 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualFixed']],
604 'total_amount' => 0,
605 'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
606 'send_receipt' => TRUE,
607 'receipt_text' => 'Receipt text',
608 'financial_type_id' => '',
609 ]);
610 $form->postProcess();
611 $this->mut->checkMailLog([], [
612 'Membership',
613 'Receipt text',
614 ]);
615 }
616
617 /**
618 * Test the submit function of the membership form on membership type change.
619 * Check if the related contribution is also updated if the minimum_fee didn't match
620 *
621 * @throws \CRM_Core_Exception
622 * @throws \CiviCRM_API3_Exception
623 */
624 public function testContributionUpdateOnMembershipTypeChange(): void {
625 // @todo figure out why financial validation fails with this test.
626 $this->isValidateFinancialsOnPostAssert = FALSE;
627 // Step 1: Create a Membership via backoffice whose with 50.00 payment
628 $form = $this->getForm();
629 $this->mut = new CiviMailUtils($this, TRUE);
630 $this->createLoggedInUser();
631 $priceSet = $this->callAPISuccess('PriceSet', 'Get', ["extends" => "CiviMember"]);
632 $form->set('priceSetId', $priceSet['id']);
633 CRM_Price_BAO_PriceSet::buildPriceSet($form);
634 $params = [
635 'cid' => $this->_individualId,
636 'join_date' => date('Y-m-d'),
637 'start_date' => '',
638 'end_date' => '',
639 // This format reflects the first being the organisation & the $this->ids['membership_type']['AnnualFixed'] being the type.
640 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualFixed']],
641 'record_contribution' => 1,
642 'total_amount' => 50,
643 'receive_date' => date('Y-m-d', time()) . ' 20:36:00',
644 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
645 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
646 //Member dues, see data.xml
647 'financial_type_id' => '2',
648 'payment_processor_id' => $this->_paymentProcessorID,
649 ];
650 $form->_contactID = $this->_individualId;
651 $form->testSubmit($params);
652 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
653 // check the membership status after partial payment, if its Pending
654 $this->assertEquals(array_search('New', CRM_Member_PseudoConstant::membershipStatus()), $membership['status_id']);
655 $contribution = $this->callAPISuccessGetSingle('Contribution', [
656 'contact_id' => $this->_individualId,
657 ]);
658 $this->assertEquals('Completed', $contribution['contribution_status']);
659 $this->assertEquals(50.00, $contribution['total_amount']);
660 $this->assertEquals(50.00, $contribution['net_amount']);
661
662 // Step 2: Change the membership type whose minimum free is less than earlier membership
663 $secondMembershipType = $this->callAPISuccess('membership_type', 'create', [
664 'domain_id' => 1,
665 'name' => 'Second Test Membership',
666 'member_of_contact_id' => $this->ids['contact']['organization'],
667 'duration_unit' => 'month',
668 'minimum_fee' => 25,
669 'duration_interval' => 1,
670 'period_type' => 'fixed',
671 'fixed_period_start_day' => '101',
672 'fixed_period_rollover_day' => '1231',
673 'relationship_type_id' => 20,
674 'financial_type_id' => 2,
675 ]);
676 Civi::settings()->set('update_contribution_on_membership_type_change', TRUE);
677 $form = $this->getForm();
678 $form->preProcess();
679 $form->_id = $membership['id'];
680 $form->set('priceSetId', $priceSet['id']);
681 CRM_Price_BAO_PriceSet::buildPriceSet($form);
682 $form->_action = CRM_Core_Action::UPDATE;
683 $params = [
684 'cid' => $this->_individualId,
685 'join_date' => date('Y-m-d'),
686 'start_date' => '',
687 'end_date' => '',
688 // This format reflects the first number being the organisation & the 25 being the type.
689 'membership_type_id' => [$this->ids['contact']['organization'], $secondMembershipType['id']],
690 'status_id' => 1,
691 'receive_date' => date('Y-m-d', time()) . ' 20:36:00',
692 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
693 //Member dues, see data.xml
694 'financial_type_id' => '2',
695 'payment_processor_id' => $this->_paymentProcessorID,
696 ];
697 $form->_contactID = $this->_individualId;
698 $form->testSubmit($params);
699 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
700 // check the membership status after partial payment, if its Pending
701 $contribution = $this->callAPISuccessGetSingle('Contribution', [
702 'contact_id' => $this->_individualId,
703 ]);
704 $payment = CRM_Contribute_BAO_Contribution::getPaymentInfo($membership['id'], 'membership', FALSE);
705 // Check the contribution status on membership type change whose minimum fee was less than earlier membership
706 $this->assertEquals('Pending refund', $contribution['contribution_status']);
707 // Earlier paid amount
708 $this->assertEquals(50, $payment['paid']);
709 // balance remaining
710 $this->assertEquals(-25, $payment['balance']);
711
712 //Update to lifetime membership.
713 $params['membership_type_id'] = [$this->ids['contact']['organization'], $this->ids['membership_type']['lifetime']];
714 $form->testSubmit($params);
715 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
716 $this->assertEquals($this->ids['membership_type']['lifetime'], $membership['membership_type_id']);
717 $this->assertTrue(empty($membership['end_date']), 'Lifetime Membership on the individual has an End date.');
718 }
719
720 /**
721 * Test the submit function of the membership form for partial payment.
722 *
723 * @param string $thousandSeparator
724 * punctuation used to refer to thousands.
725 *
726 * @throws \CRM_Core_Exception
727 * @throws \CiviCRM_API3_Exception
728 * @dataProvider getThousandSeparators
729 */
730 public function testSubmitPartialPayment(string $thousandSeparator): void {
731 $this->setCurrencySeparators($thousandSeparator);
732 // Step 1: submit a partial payment for a membership via backoffice
733 $form = $this->getForm();
734 $form->preProcess();
735 $this->mut = new CiviMailUtils($this, TRUE);
736 $this->createLoggedInUser();
737 $priceSet = $this->callAPISuccess('PriceSet', 'Get', ["extends" => "CiviMember"]);
738 $form->set('priceSetId', $priceSet['id']);
739
740 CRM_Price_BAO_PriceSet::buildPriceSet($form);
741 $params = [
742 'contact_id' => $this->_individualId,
743 'join_date' => date('Y-m-d'),
744 'start_date' => '',
745 'end_date' => '',
746 // This format reflects the first number being the organisation & the second being the type.
747 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualFixed']],
748 'receive_date' => date('Y-m-d', time()) . ' 20:36:00',
749 'record_contribution' => 1,
750 'total_amount' => $this->formatMoneyInput(50),
751 'payment_instrument_id' => array_search('Check', $this->paymentInstruments, TRUE),
752 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'),
753 //Member dues, see data.xml
754 'financial_type_id' => '2',
755 'payment_processor_id' => $this->_paymentProcessorID,
756 ];
757 $form->_contactID = $this->_individualId;
758 $form->testSubmit($params);
759 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
760 // check the membership status after partial payment, if its Pending
761 $this->assertEquals(array_search('Pending', CRM_Member_PseudoConstant::membershipStatus(), TRUE), $membership['status_id']);
762 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId]);
763 $this->callAPISuccess('Payment', 'create', ['contribution_id' => $contribution['id'], 'total_amount' => 25, 'payment_instrument_id' => 'Cash']);
764 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $contribution['id']]);
765 $this->assertEquals('Partially paid', $contribution['contribution_status']);
766
767 // Step 2: submit the other half of the partial payment
768 // via AdditionalPayment form to complete the related contribution
769 $form = new CRM_Contribute_Form_AdditionalPayment();
770 $submitParams = [
771 'contribution_id' => $contribution['contribution_id'],
772 'contact_id' => $this->_individualId,
773 'total_amount' => $this->formatMoneyInput(25),
774 'currency' => 'USD',
775 'financial_type_id' => 2,
776 'receive_date' => '2015-04-21 23:27:00',
777 'trxn_date' => '2017-04-11 13:05:11',
778 'payment_processor_id' => 0,
779 'payment_instrument_id' => array_search('Check', $this->paymentInstruments, TRUE),
780 'check_number' => 'check-12345',
781 ];
782 $form->cid = $this->_individualId;
783 $form->testSubmit($submitParams);
784 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
785 // check the membership status after additional payment, if its changed to 'New'
786 $this->assertEquals(array_search('New', CRM_Member_PseudoConstant::membershipStatus(), TRUE), $membership['status_id']);
787
788 // check the contribution status and net amount after additional payment
789 $contribution = $this->callAPISuccessGetSingle('Contribution', [
790 'contact_id' => $this->_individualId,
791 ]);
792 $this->assertEquals('Completed', $contribution['contribution_status']);
793 $this->validateAllPayments();
794 }
795
796 /**
797 * Test the submit function of the membership form.
798 *
799 * @throws \API_Exception
800 * @throws \CRM_Core_Exception
801 * @throws \CiviCRM_API3_Exception
802 */
803 public function testSubmitRecur(): void {
804 CRM_Core_Session::singleton()->getStatus(TRUE);
805 $pendingVal = $this->callAPISuccessGetValue('OptionValue', [
806 'return' => 'id',
807 'option_group_id' => 'contribution_status',
808 'label' => 'Pending Label**',
809 ]);
810 //Update label for Pending contribution status.
811 $this->callAPISuccess('OptionValue', 'create', [
812 'id' => $pendingVal,
813 'label' => 'PendingEdited',
814 ]);
815
816 $this->callAPISuccess('MembershipType', 'create', [
817 'id' => $this->ids['membership_type']['AnnualFixed'],
818 'duration_unit' => 'month',
819 'duration_interval' => 1,
820 'auto_renew' => 1,
821 ]);
822 $params = $this->getBaseSubmitParams();
823 // Change financial_type_id to test our override flows through to the line item.
824 $params['financial_type_id'] = FinancialType::get(FALSE)->addWhere('id', '!=', $params['financial_type_id'])->addSelect('id')->execute()->first()['id'];
825 $form = $this->getForm($params);
826 $this->createLoggedInUser();
827 $form->_mode = 'test';
828 $form->_contactID = $this->_individualId;
829 $form->testSubmit($params);
830 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
831 $this->callAPISuccessGetCount('ContributionRecur', ['contact_id' => $this->_individualId], 1);
832
833 $contribution = $this->callAPISuccess('Contribution', 'get', [
834 'contact_id' => $this->_individualId,
835 'is_test' => TRUE,
836 ]);
837
838 //Check if Membership Payment is recorded.
839 $this->callAPISuccessGetCount('MembershipPayment', [
840 'membership_id' => $membership['id'],
841 'contribution_id' => $contribution['id'],
842 ], 1);
843
844 // CRM-16992.
845 $this->callAPISuccessGetCount('LineItem', [
846 'entity_id' => $membership['id'],
847 'entity_table' => 'civicrm_membership',
848 'contribution_id' => $contribution['id'],
849 'financial_type_id' => $params['financial_type_id'],
850 ], 1);
851 $this->assertEquals([
852 [
853 'text' => 'AnnualFixed membership for Mr. Anthony Anderson II has been added. The new membership End Date is ' . date('F jS, Y', strtotime('last day of this month')) . '.',
854 'title' => 'Complete',
855 'type' => 'success',
856 'options' => NULL,
857 ],
858 ], CRM_Core_Session::singleton()->getStatus());
859 }
860
861 /**
862 * Test submit recurring with two line items.
863 *
864 * @throws \CRM_Core_Exception
865 * @throws \CiviCRM_API3_Exception
866 * @throws \API_Exception
867 */
868 public function testSubmitRecurTwoRows(): void {
869 $pfvIDs = $this->createMembershipPriceSet();
870 MembershipType::update()
871 ->addWhere('id', '=', $this->ids['membership_type']['AnnualRollingOrg2'])
872 ->setValues(['frequency_interval' => 1, 'frequency_unit' => 'month', 'auto_renew' => 1])->execute();
873 $form = $this->getForm();
874 $form->_mode = 'live';
875 $priceParams = [
876 'price_' . $this->getPriceFieldID() => $pfvIDs,
877 'price_set_id' => $this->getPriceSetID(),
878 'membership_type_id' => NULL,
879 // Set financial type id to null to check it is retrieved from the price set.
880 'financial_type_id' => NULL,
881 ];
882 $form->testSubmit(array_merge($this->getBaseSubmitParams(), $priceParams));
883 $memberships = $this->callAPISuccess('Membership', 'get')['values'];
884 $this->assertCount(2, $memberships);
885 $this->callAPISuccessGetSingle('Contribution', ['financial_type_id' => 1]);
886 $this->callAPISuccessGetCount('MembershipPayment', [], 2);
887 $lines = $this->callAPISuccess('LineItem', 'get', ['sequential' => 1])['values'];
888 $this->assertCount(2, $lines);
889 $this->assertEquals('civicrm_membership', $lines[0]['entity_table']);
890 $this->assertEquals('civicrm_membership', $lines[1]['entity_table']);
891
892 }
893
894 /**
895 * CRM-20946: Test the financial entires especially the reversed amount,
896 * after related Contribution is cancelled
897 *
898 * @throws \CRM_Core_Exception
899 * @throws \CiviCRM_API3_Exception
900 */
901 public function testFinancialEntriesOnCancelledContribution(): void {
902 // @todo figure out why financial validation fails with this test.
903 $this->isValidateFinancialsOnPostAssert = FALSE;
904 // Create two memberships for individual $this->_individualId, via a price set in the back end.
905 $this->createTwoMembershipsViaPriceSetInBackEnd($this->_individualId);
906
907 // cancel the related contribution via API
908 $contribution = $this->callAPISuccessGetSingle('Contribution', [
909 'contact_id' => $this->_individualId,
910 'contribution_status_id' => 2,
911 ]);
912 $this->callAPISuccess('Contribution', 'create', [
913 'id' => $contribution['id'],
914 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_DAO_Contribution', 'contribution_status_id', 'Cancelled'),
915 ]);
916
917 // fetch financial_trxn ID of the related contribution
918 $sql = "SELECT financial_trxn_id
919 FROM civicrm_entity_financial_trxn
920 WHERE entity_id = %1 AND entity_table = 'civicrm_contribution'
921 ORDER BY id DESC
922 LIMIT 1
923 ";
924 $financialTrxnID = CRM_Core_DAO::singleValueQuery($sql, [1 => [$contribution['id'], 'Int']]);
925
926 // fetch entity_financial_trxn records and compare their cancelled records
927 $result = $this->callAPISuccess('EntityFinancialTrxn', 'Get', [
928 'financial_trxn_id' => $financialTrxnID,
929 'entity_table' => 'civicrm_financial_item',
930 ]);
931 // compare the reversed amounts of respective memberships after cancelling contribution
932 $cancelledMembershipAmounts = [
933 -259.00,
934 -20.00,
935 ];
936 $count = 0;
937 foreach ($result['values'] as $record) {
938 $this->assertEquals($cancelledMembershipAmounts[$count], $record['amount']);
939 $count++;
940 }
941 }
942
943 /**
944 * Test membership with soft credits.
945 */
946 public function testMembershipSoftCredit() {
947 $this->_softIndividualId = $this->individualCreate();
948
949 $form = $this->getForm();
950 $form->preProcess();
951 $this->createLoggedInUser();
952 $params = $this->getBaseSubmitParams();
953 unset($params['auto_renew'], $params['is_recur']);
954 $params['record_contribution'] = TRUE;
955 $params['soft_credit_type_id'] = $this->callAPISuccessGetValue('OptionValue', [
956 'return' => "value",
957 'name' => "Gift",
958 'option_group_id' => "soft_credit_type",
959 ]);
960 $params['soft_credit_contact_id'] = $this->_softIndividualId;
961 $form->_contactID = $this->_individualId;
962 // $form->_mode = 'test';
963 $form->testSubmit($params);
964 // Membership is created on main contact.
965 $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
966
967 // Verify is main contribution is created on soft contact.
968 $contribution = $this->callAPISuccessGetSingle('Contribution', [
969 'contact_id' => $this->_softIndividualId,
970 ]);
971 $this->assertEquals($contribution['soft_credit'][1]['contact_id'], $this->_individualId);
972
973 // Verify if soft credit is created.
974 $this->callAPISuccessGetSingle('ContributionSoft', [
975 'contact_id' => $this->_individualId,
976 'contribution_id' => $contribution['id'],
977 ]);
978 }
979
980 /**
981 * Test the submit function of the membership form.
982 *
983 * @throws \CRM_Core_Exception
984 */
985 public function testSubmitPayLaterWithBilling(): void {
986 $form = $this->getForm();
987 $this->createLoggedInUser();
988 $params = [
989 'contact_id' => $this->_individualId,
990 'join_date' => date('Y-m-d'),
991 'start_date' => '',
992 'end_date' => '',
993 // This format reflects the first number being the organisation & the second being the type.
994 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualFixed']],
995 'auto_renew' => '0',
996 'max_related' => '',
997 'num_terms' => '2',
998 'source' => '',
999 'total_amount' => '50.00',
1000 //Member dues, see data.xml
1001 'financial_type_id' => '2',
1002 'soft_credit_type_id' => '',
1003 'soft_credit_contact_id' => '',
1004 'payment_instrument_id' => 4,
1005 'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
1006 'receipt_text_signup' => 'Thank you text',
1007 'payment_processor_id' => $this->_paymentProcessorID,
1008 'record_contribution' => TRUE,
1009 'trxn_id' => 777,
1010 'contribution_status_id' => 2,
1011 'billing_first_name' => 'Test',
1012 'billing_middle_name' => 'Last',
1013 'billing_street_address-5' => '10 Test St',
1014 'billing_city-5' => 'Test',
1015 'billing_state_province_id-5' => '1003',
1016 'billing_postal_code-5' => '90210',
1017 'billing_country_id-5' => '1228',
1018 ];
1019 $form->_contactID = $this->_individualId;
1020
1021 $form->testSubmit($params);
1022 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
1023 $contribution = $this->callAPISuccessGetSingle('Contribution', [
1024 'contact_id' => $this->_individualId,
1025 'contribution_status_id' => 2,
1026 ]);
1027 $this->assertEquals($contribution['trxn_id'], 777);
1028
1029 $this->callAPISuccessGetCount('LineItem', [
1030 'entity_id' => $membership['id'],
1031 'entity_table' => 'civicrm_membership',
1032 'contribution_id' => $contribution['id'],
1033 ], 1);
1034 $this->callAPISuccessGetSingle('address', [
1035 'contact_id' => $this->_individualId,
1036 'street_address' => '10 Test St',
1037 'postal_code' => 90210,
1038 ]);
1039 }
1040
1041 /**
1042 * Test if membership is updated to New after contribution
1043 * is updated from Partially paid to Completed.
1044 *
1045 * @throws \CRM_Core_Exception
1046 * @throws \CiviCRM_API3_Exception
1047 */
1048 public function testSubmitUpdateMembershipFromPartiallyPaid(): void {
1049 $memStatus = CRM_Member_BAO_Membership::buildOptions('status_id', 'validate');
1050
1051 //Perform a pay later membership contribution.
1052 $this->testSubmitPayLaterWithBilling();
1053 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
1054 $this->assertEquals($membership['status_id'], array_search('Pending', $memStatus));
1055 $contribution = $this->callAPISuccessGetSingle('MembershipPayment', [
1056 'membership_id' => $membership['id'],
1057 ]);
1058 $prevContribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $contribution['id']]);
1059 $this->callAPISuccess('Payment', 'create', [
1060 'contribution_id' => $contribution['contribution_id'],
1061 'payment_instrument_id' => 'Cash',
1062 'total_amount' => 5,
1063 ]);
1064
1065 $submitParams = [
1066 'id' => $contribution['contribution_id'],
1067 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
1068 ];
1069 $fields = ['total_amount', 'net_amount', 'financial_type_id', 'receive_date', 'contact_id', 'payment_instrument_id'];
1070 foreach ($fields as $val) {
1071 $submitParams[$val] = $prevContribution[$val];
1072 }
1073 $_REQUEST['action'] = 'update';
1074 $_REQUEST['id'] = $contribution['contribution_id'];
1075 // Complete the contribution from offline form.
1076 $form = $this->getContributionForm($submitParams);
1077 $form->postProcess();
1078
1079 //Check if Membership is updated to New.
1080 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
1081 $this->assertEquals($membership['status_id'], array_search('New', $memStatus));
1082 }
1083
1084 /**
1085 * Test the submit function of the membership form.
1086 *
1087 * @throws \CiviCRM_API3_Exception
1088 * @throws \CRM_Core_Exception
1089 */
1090 public function testSubmitRecurCompleteInstant(): void {
1091 $mut = new CiviMailUtils($this, TRUE);
1092 /* @var \CRM_Core_Payment_Dummy $processor */
1093 $processor = Civi\Payment\System::singleton()->getById($this->_paymentProcessorID);
1094 $processor->setDoDirectPaymentResult([
1095 'payment_status_id' => 1,
1096 'trxn_id' => 'kettles boil water',
1097 'fee_amount' => .14,
1098 ]);
1099 $processorDetail = $processor->getPaymentProcessor();
1100 $this->callAPISuccess('MembershipType', 'create', [
1101 'id' => $this->ids['membership_type']['AnnualFixed'],
1102 'duration_unit' => 'month',
1103 'duration_interval' => 1,
1104 'auto_renew' => 1,
1105 ]);
1106 $form = $this->getForm($this->getBaseSubmitParams());
1107 $this->createLoggedInUser();
1108 $form->_mode = 'test';
1109 $form->_contactID = $this->_individualId;
1110 $form->testSubmit();
1111 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
1112 $this->callAPISuccessGetCount('ContributionRecur', ['contact_id' => $this->_individualId], 1);
1113
1114 $contribution = $this->callAPISuccess('Contribution', 'getsingle', [
1115 'contact_id' => $this->_individualId,
1116 'is_test' => TRUE,
1117 ]);
1118
1119 $this->assertEquals(.14, $contribution['fee_amount']);
1120 $this->assertEquals('kettles boil water', $contribution['trxn_id']);
1121 $this->assertEquals($processorDetail['payment_instrument_id'], $contribution['payment_instrument_id']);
1122
1123 $this->callAPISuccessGetCount('LineItem', [
1124 'entity_id' => $membership['id'],
1125 'entity_table' => 'civicrm_membership',
1126 'contribution_id' => $contribution['id'],
1127 ], 1);
1128 $mut->checkMailLog([
1129 '===========================================================
1130 Billing Name and Address
1131 ===========================================================
1132 Test Last
1133 10 Test St
1134 Test, AR 90210
1135 US',
1136 '===========================================================
1137 Membership Information
1138 ===========================================================
1139 Membership Type: AnnualFixed
1140 Membership Start Date: ',
1141 '===========================================================
1142 Credit Card Information
1143 ===========================================================
1144 Visa
1145 ************1111
1146 Expires: ',
1147 ]);
1148 $mut->stop();
1149
1150 }
1151
1152 /**
1153 * CRM-20955, CRM-20966:
1154 * Test creating two memberships with inheritance via price set in the back end,
1155 * checking that the correct primary & secondary memberships, contributions, line items
1156 * & membership_payment records are created.
1157 * Uses some data from tests/phpunit/CRM/Member/Form/dataset/data.xml .
1158 *
1159 * @throws \CRM_Core_Exception
1160 * @throws \CiviCRM_API3_Exception
1161 */
1162 public function testTwoInheritedMembershipsViaPriceSetInBackend(): void {
1163 // @todo figure out why financial validation fails with this test.
1164 $this->isValidateFinancialsOnPostAssert = FALSE;
1165 // Create an organization and give it a "Member of" relationship to $this->_individualId.
1166 $orgID = $this->organizationCreate();
1167 $relationship = $this->callAPISuccess('Relationship', 'create', [
1168 'contact_id_a' => $this->_individualId,
1169 'contact_id_b' => $orgID,
1170 'relationship_type_id' => $this->ids['relationship_type']['member'],
1171 'is_active' => 1,
1172 ]);
1173
1174 // Create two memberships for the organization, via a price set in the back end.
1175 $this->createTwoMembershipsViaPriceSetInBackEnd($orgID);
1176
1177 // Check the primary memberships on the organization.
1178 $orgMembershipResult = $this->callAPISuccess('membership', 'get', [
1179 'contact_id' => $orgID,
1180 ]);
1181 $this->assertEquals(2, $orgMembershipResult['count'], '2 primary memberships should have been created on the organization.');
1182 $primaryMembershipIds = [];
1183 foreach ($orgMembershipResult['values'] as $membership) {
1184 $primaryMembershipIds[] = $membership['id'];
1185 $this->assertTrue(empty($membership['owner_membership_id']), 'Membership on the organization has owner_membership_id so is inherited.');
1186 }
1187
1188 // CRM-20955: check that correct inherited memberships were created for the individual,
1189 // for both of the primary memberships.
1190 $individualMembershipResult = $this->callAPISuccess('membership', 'get', [
1191 'contact_id' => $this->_individualId,
1192 ]);
1193 $this->assertEquals(2, $individualMembershipResult['count'], "2 inherited memberships should have been created on the individual.");
1194 foreach ($individualMembershipResult['values'] as $membership) {
1195 $this->assertNotEmpty($membership['owner_membership_id'], "Membership on the individual lacks owner_membership_id so is not inherited.");
1196 $this->assertNotContains($membership['id'], $primaryMembershipIds, "Inherited membership id should not be the id of a primary membership.");
1197 $this->assertContains($membership['owner_membership_id'], $primaryMembershipIds, "Inherited membership owner_membership_id should be the id of a primary membership.");
1198 }
1199
1200 // CRM-20966: check that the correct membership contribution, line items
1201 // & membership_payment records were created for the organization.
1202 $contributionResult = $this->callAPISuccess('contribution', 'get', [
1203 'contact_id' => $orgID,
1204 'sequential' => 1,
1205 'api.line_item.get' => [],
1206 'api.membership_payment.get' => [],
1207 ]);
1208 $this->assertEquals(1, $contributionResult['count'], "One contribution should have been created for the organization's memberships.");
1209
1210 $this->assertEquals(2, $contributionResult['values'][0]['api.line_item.get']['count'], "2 line items should have been created for the organization's memberships.");
1211 foreach ($contributionResult['values'][0]['api.line_item.get']['values'] as $lineItem) {
1212 $this->assertEquals('civicrm_membership', $lineItem['entity_table'], "Membership line item's entity_table should be 'civicrm_membership'.");
1213 $this->assertContains($lineItem['entity_id'], $primaryMembershipIds, "Membership line item's entity_id should be the id of a primary membership.");
1214 }
1215
1216 $this->assertEquals(2, $contributionResult['values'][0]['api.membership_payment.get']['count'], "2 membership payment records should have been created for the organization's memberships.");
1217 foreach ($contributionResult['values'][0]['api.membership_payment.get']['values'] as $membershipPayment) {
1218 $this->assertEquals($contributionResult['values'][0]['id'], $membershipPayment['contribution_id'], "membership payment's contribution ID should be the ID of the organization's membership contribution.");
1219 $this->assertContains($membershipPayment['membership_id'], $primaryMembershipIds, "membership payment's membership ID should be the ID of a primary membership.");
1220 }
1221 // Check for orphan line items.
1222 $this->callAPISuccessGetCount('LineItem', [], 2);
1223
1224 // CRM-20966: check that deleting relationship used for inheritance does not delete contribution.
1225 $this->callAPISuccess('relationship', 'delete', [
1226 'id' => $relationship['id'],
1227 ]);
1228
1229 $contributionResultAfterRelationshipDelete = $this->callAPISuccess('contribution', 'get', [
1230 'id' => $contributionResult['values'][0]['id'],
1231 'contact_id' => $orgID,
1232 ]);
1233 $this->assertEquals(1, $contributionResultAfterRelationshipDelete['count'], "Contribution has been wrongly deleted.");
1234 }
1235
1236 /**
1237 * dev/core/issues/860:
1238 * Test creating two memberships via price set in the back end with a discount,
1239 * checking that the line items have correct amounts.
1240 *
1241 * @throws \CRM_Core_Exception
1242 * @throws \CiviCRM_API3_Exception
1243 */
1244 public function testTwoMembershipsViaPriceSetInBackendWithDiscount(): void {
1245 // @todo figure out how to fix to pass valid financials.
1246 $this->isValidateFinancialsOnPostAssert = FALSE;
1247 // Register buildAmount hook to apply discount.
1248 $this->hookClass->setHook('civicrm_buildAmount', [$this, 'buildAmountMembershipDiscount']);
1249 $this->enableTaxAndInvoicing();
1250 $this->addTaxAccountToFinancialType(2);
1251 // Create two memberships for individual $this->_individualId, via a price set in the back end.
1252 $this->createTwoMembershipsViaPriceSetInBackEnd($this->_individualId);
1253 $contribution = $this->callAPISuccessGetSingle('Contribution', [
1254 'contact_id' => $this->_individualId,
1255 ]);
1256
1257 // Note: we can't check for the contribution total being discounted, because the total is set
1258 // when the contribution is created via $form->testSubmit(), but buildAmount isn't called
1259 // until testSubmit() runs. Fixing that might involve making testSubmit() more sophisticated,
1260 // or just hacking total_amount for this case.
1261
1262 $lineItemResult = $this->callAPISuccess('LineItem', 'get', [
1263 'contribution_id' => $contribution['id'],
1264 ]);
1265 $this->assertEquals(2, $lineItemResult['count']);
1266 $discountedItems = 0;
1267 foreach ($lineItemResult['values'] as $lineItem) {
1268 $this->assertEquals($lineItem['line_total'] * .1, $lineItem['tax_amount']);
1269 if (CRM_Utils_String::startsWith($lineItem['label'], 'Long Haired Goat')) {
1270 $this->assertEquals(15.0, $lineItem['line_total']);
1271 $this->assertEquals('Long Haired Goat - one leg free!', $lineItem['label']);
1272 $discountedItems++;
1273 }
1274 }
1275 $this->assertEquals(1, $discountedItems);
1276 }
1277
1278 /**
1279 * Implements hook_civicrm_buildAmount() for testTwoMembershipsViaPriceSetInBackendWithDiscount().
1280 */
1281 public function buildAmountMembershipDiscount($pageType, &$form, &$amount) {
1282 foreach ($amount as $id => $priceField) {
1283 if (is_array($priceField['options'])) {
1284 foreach ($priceField['options'] as $optionId => $option) {
1285 if ($option['membership_type_id'] == $this->ids['membership_type']['AnnualRolling']) {
1286 // Long Haired Goat membership discount.
1287 $amount[$id]['options'][$optionId]['amount'] = $option['amount'] * 0.75;
1288 $amount[$id]['options'][$optionId]['label'] = $option['label'] . ' - one leg free!';
1289 }
1290 }
1291 }
1292 }
1293 }
1294
1295 /**
1296 * Get a membership form object.
1297 *
1298 * We need to instantiate the form to run preprocess, which means we have to
1299 * trick it about the request method.
1300 *
1301 * @param array $formValues
1302 *
1303 * @return \CRM_Member_Form_Membership
1304 * @throws \CRM_Core_Exception
1305 * @throws \CiviCRM_API3_Exception
1306 */
1307 protected function getForm(array $formValues = []): CRM_Member_Form_Membership {
1308 if (isset($_REQUEST['cid'])) {
1309 unset($_REQUEST['cid']);
1310 }
1311 /* @var CRM_Member_Form_Membership $form*/
1312 $form = $this->getFormObject('CRM_Member_Form_Membership', $formValues);
1313 $form->preProcess();
1314 return $form;
1315 }
1316
1317 /**
1318 * @return array
1319 */
1320 protected function getBaseSubmitParams(): array {
1321 return [
1322 'contact_id' => $this->_individualId,
1323 'cid' => $this->_individualId,
1324 'price_set_id' => 0,
1325 'join_date' => date('Y-m-d'),
1326 'start_date' => '',
1327 'end_date' => '',
1328 'campaign_id' => '',
1329 // This format reflects the first number being the organisation & the second being the type.
1330 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualFixed']],
1331 'auto_renew' => '1',
1332 'is_recur' => 1,
1333 'max_related' => 0,
1334 'num_terms' => '1',
1335 'source' => '',
1336 'total_amount' => '77.00',
1337 //Member dues, see data.xml
1338 'financial_type_id' => '2',
1339 'soft_credit_type_id' => 11,
1340 'soft_credit_contact_id' => '',
1341 'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
1342 'receipt_text' => 'Thank you text',
1343 'payment_processor_id' => $this->_paymentProcessorID,
1344 'credit_card_number' => '4111111111111111',
1345 'cvv2' => '123',
1346 'credit_card_exp_date' => [
1347 'M' => '9',
1348 'Y' => date('Y') + 1,
1349 ],
1350 'credit_card_type' => 'Visa',
1351 'billing_first_name' => 'Test',
1352 'billing_middle_name' => 'Last',
1353 'billing_street_address-5' => '10 Test St',
1354 'billing_city-5' => 'Test',
1355 'billing_state_province_id-5' => '1003',
1356 'billing_postal_code-5' => '90210',
1357 'billing_country_id-5' => '1228',
1358 'send_receipt' => 1,
1359 ];
1360 }
1361
1362 /**
1363 * Scenario builder:
1364 * create two memberships for the same individual, via a price set in the back end.
1365 *
1366 * @param int $contactId Id of contact on which the memberships will be created.
1367 *
1368 * @throws \CRM_Core_Exception
1369 * @throws \CiviCRM_API3_Exception
1370 */
1371 protected function createTwoMembershipsViaPriceSetInBackEnd($contactId): void {
1372 $form = $this->getForm();
1373 $form->preProcess();
1374 $this->createLoggedInUser();
1375 $pfvIDs = $this->createMembershipPriceSet();
1376
1377 // register for both of these memberships via backoffice membership form submission
1378 $params = [
1379 'cid' => $contactId,
1380 'contact_id' => $contactId,
1381 'join_date' => date('Y-m-d'),
1382 'start_date' => '',
1383 'end_date' => '',
1384 "price_" . $this->getPriceFieldID() => $pfvIDs,
1385 'price_set_id' => $this->getPriceSetID(),
1386 'membership_type_id' => [1 => 0],
1387 'auto_renew' => '0',
1388 'max_related' => '',
1389 'num_terms' => '2',
1390 'source' => '',
1391 'total_amount' => '30.00',
1392 //Member dues, see data.xml
1393 'financial_type_id' => '2',
1394 'soft_credit_type_id' => '',
1395 'soft_credit_contact_id' => '',
1396 'payment_instrument_id' => 4,
1397 'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
1398 'receipt_text_signup' => 'Thank you text',
1399 'payment_processor_id' => $this->_paymentProcessorID,
1400 'record_contribution' => TRUE,
1401 'trxn_id' => 777,
1402 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_DAO_Contribution', 'contribution_status_id', 'Pending'),
1403 'billing_first_name' => 'Test',
1404 'billing_middle_name' => 'Last',
1405 'billing_street_address-5' => '10 Test St',
1406 'billing_city-5' => 'Test',
1407 'billing_state_province_id-5' => '1003',
1408 'billing_postal_code-5' => '90210',
1409 'billing_country_id-5' => '1228',
1410 ];
1411 $form->testSubmit($params);
1412 }
1413
1414 /**
1415 * Test membership status overrides when contribution is cancelled.
1416 *
1417 * @throws \CRM_Core_Exception
1418 */
1419 public function testContributionFormStatusUpdate(): void {
1420 // @todo figure out why financial validation fails with this test.
1421 $this->isValidateFinancialsOnPostAssert = FALSE;
1422 $this->_contactID = $this->ids['Contact']['order'] = $this->createLoggedInUser();
1423 $this->createContributionAndMembershipOrder();
1424
1425 $params = [
1426 'total_amount' => 50,
1427 'financial_type_id' => 2,
1428 'contact_id' => $this->_contactID,
1429 'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
1430 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Cancelled'),
1431 ];
1432 $_REQUEST['action'] = 'update';
1433 $_REQUEST['id'] = $this->ids['Contribution'][0];
1434 $form = $this->getContributionForm($params);
1435 $form->postProcess();
1436 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_contactID]);
1437
1438 //Assert membership status overrides when the contribution cancelled.
1439 $this->assertEquals(TRUE, $membership['is_override']);
1440 $this->assertEquals($membership['status_id'], $this->callAPISuccessGetValue('MembershipStatus', [
1441 'return' => 'id',
1442 'name' => 'Cancelled',
1443 ]));
1444 }
1445
1446 /**
1447 * Get the contribution form object.
1448 *
1449 * @param array $formValues
1450 *
1451 * @return \CRM_Contribute_Form_Contribution
1452 */
1453 protected function getContributionForm(array $formValues): CRM_Contribute_Form_Contribution {
1454 /* @var CRM_Contribute_Form_Contribution $form */
1455 $form = $this->getFormObject('CRM_Contribute_Form_Contribution', $formValues);
1456 $form->buildForm();
1457 return $form;
1458 }
1459
1460 /**
1461 * CRM-21656: Test the submit function of the membership form if Sales Tax is enabled.
1462 * This test simulates what happens when one hits Edit on a Contribution that has both LineItems and Sales Tax components
1463 * Without making any Edits -> check that the LineItem data remain the same
1464 * In addition (a data-integrity check) -> check that the LineItem data add up to the data at the Contribution level
1465 *
1466 * @throws \CRM_Core_Exception
1467 * @throws \CiviCRM_API3_Exception
1468 */
1469 public function testLineItemAmountOnSalesTax(): void {
1470 $this->enableTaxAndInvoicing();
1471 $this->addTaxAccountToFinancialType(2);
1472 $form = $this->getForm();
1473 $form->preProcess();
1474 $this->mut = new CiviMailUtils($this, TRUE);
1475 $this->createLoggedInUser();
1476 $priceSet = $this->callAPISuccess('PriceSet', 'Get', ['extends' => 'CiviMember']);
1477 $form->set('priceSetId', $priceSet['id']);
1478 // we are simulating the creation of a Price Set in Administer -> CiviContribute -> Manage Price Sets so set is_quick_config = 0
1479 $this->callAPISuccess('PriceSet', 'Create', ['id' => $priceSet['id'], 'is_quick_config' => 0]);
1480 // clean the price options static variable to repopulate the options, in order to fetch tax information
1481 \Civi::$statics['CRM_Price_BAO_PriceField']['priceOptions'] = NULL;
1482 CRM_Price_BAO_PriceSet::buildPriceSet($form);
1483 // rebuild the price set form variable to include the tax information against each price options
1484 $form->_priceSet = current(CRM_Price_BAO_PriceSet::getSetDetail($priceSet['id']));
1485 $params = [
1486 'cid' => $this->_individualId,
1487 'join_date' => date('Y-m-d'),
1488 'start_date' => '',
1489 'end_date' => '',
1490 // This format reflects the first number being the organisation & the second being the type.
1491 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualFixed']],
1492 'record_contribution' => 1,
1493 'total_amount' => 55,
1494 'receive_date' => date('Y-m-d') . ' 20:36:00',
1495 'payment_instrument_id' => array_search('Check', $this->paymentInstruments, TRUE),
1496 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
1497 //Member dues, see data.xml
1498 'financial_type_id' => 2,
1499 'payment_processor_id' => $this->_paymentProcessorID,
1500 ];
1501 $form->_contactID = $this->_individualId;
1502 $form->testSubmit($params);
1503
1504 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
1505 $lineItem = $this->callAPISuccessGetSingle('LineItem', ['entity_id' => $membership['id'], 'entity_table' => 'civicrm_membership']);
1506 $this->assertEquals(1, $lineItem['qty']);
1507 $this->assertEquals(50.00, $lineItem['unit_price']);
1508 $this->assertEquals(50.00, $lineItem['line_total']);
1509 $this->assertEquals(5.00, $lineItem['tax_amount']);
1510
1511 $_REQUEST['id'] = $lineItem['contribution_id'];
1512 $_REQUEST['context'] = 'membership';
1513 // Simply save the 'Edit Contribution' form
1514 $form = $this->getFormObject('CRM_Contribute_Form_Contribution', [
1515 'contact_id' => $this->_individualId,
1516 'financial_type_id' => 2,
1517 'total_amount' => 55,
1518 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
1519 ]);
1520 $form->buildForm();
1521 $form->postProcess();
1522
1523 // ensure that the LineItem data remain the same
1524 $lineItem = $this->callAPISuccessGetSingle('LineItem', ['entity_id' => $membership['id'], 'entity_table' => 'civicrm_membership']);
1525 $this->assertEquals(1, $lineItem['qty']);
1526 $this->assertEquals(50.00, $lineItem['unit_price']);
1527 $this->assertEquals(50.00, $lineItem['line_total']);
1528 $this->assertEquals(5.00, $lineItem['tax_amount']);
1529
1530 // ensure that the LineItem data add up to the data at the Contribution level
1531 $contribution = $this->callAPISuccessGetSingle('Contribution',
1532 [
1533 'contribution_id' => 1,
1534 'return' => ['tax_amount', 'total_amount'],
1535 ]
1536 );
1537 $this->assertEquals($contribution['total_amount'], $lineItem['line_total'] + $lineItem['tax_amount']);
1538 $this->assertEquals($contribution['tax_amount'], $lineItem['tax_amount']);
1539
1540 $financialItems = $this->callAPISuccess('FinancialItem', 'get', []);
1541 $financialItems_sum = 0;
1542 foreach ($financialItems['values'] as $financialItem) {
1543 $financialItems_sum += $financialItem['amount'];
1544 }
1545 $this->assertEquals($contribution['total_amount'], $financialItems_sum);
1546 }
1547
1548 /**
1549 * Test that membership end_date is correct for multiple terms for pending contribution
1550 *
1551 * @throws CiviCRM_API3_Exception
1552 * @throws \CRM_Core_Exception
1553 * @throws \Exception
1554 */
1555 public function testCreatePendingWithMultipleTerms() {
1556 CRM_Core_Session::singleton()->getStatus(TRUE);
1557 $this->mut = new CiviMailUtils($this, TRUE);
1558 $this->createLoggedInUser();
1559 $membershipTypeAnnualRolling = $this->callAPISuccess('membership_type', 'create', [
1560 'domain_id' => 1,
1561 'name' => 'AnnualRollingNew',
1562 'member_of_contact_id' => $this->ids['contact']['organization'],
1563 'duration_unit' => 'year',
1564 'minimum_fee' => 50,
1565 'duration_interval' => 1,
1566 'period_type' => 'rolling',
1567 'relationship_type_id' => 20,
1568 'relationship_direction' => 'b_a',
1569 'financial_type_id' => 2,
1570 ]);
1571 $params = [
1572 'cid' => $this->_individualId,
1573 'join_date' => date('Y-m-d'),
1574 'start_date' => '',
1575 'end_date' => '',
1576 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'),
1577 'membership_type_id' => [$this->ids['contact']['organization'], $membershipTypeAnnualRolling['id']],
1578 'max_related' => '',
1579 'num_terms' => '3',
1580 'record_contribution' => 1,
1581 'source' => '',
1582 'total_amount' => $this->formatMoneyInput(150.00),
1583 'financial_type_id' => '2',
1584 'soft_credit_type_id' => '11',
1585 'soft_credit_contact_id' => '',
1586 'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
1587 'receipt_text' => '',
1588 ];
1589 $form = $this->getForm();
1590 $form->preProcess();
1591 $form->_contactID = $this->_individualId;
1592 $form->testSubmit($params);
1593 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
1594 $contribution = $this->callAPISuccess('Contribution', 'get', [
1595 'contact_id' => $this->_individualId,
1596 ]);
1597 $endDate = (new DateTime(date('Y-m-d')))->modify('+3 years')->modify('-1 day');
1598 $endDate = $endDate->format("Y-m-d");
1599
1600 $this->assertEquals($endDate, $membership['end_date'], 'Membership end date should be ' . $endDate);
1601 $this->assertEquals(1, count($contribution['values']), 'Pending contribution should be created.');
1602 $contribution = $contribution['values'][$contribution['id']];
1603 $additionalPaymentForm = new CRM_Contribute_Form_AdditionalPayment();
1604 $additionalPaymentForm->testSubmit([
1605 'total_amount' => 150.00,
1606 'trxn_date' => date("Y-m-d H:i:s"),
1607 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
1608 'check_number' => 'check-12345',
1609 'trxn_id' => '',
1610 'currency' => 'USD',
1611 'fee_amount' => '',
1612 'financial_type_id' => 1,
1613 'net_amount' => '',
1614 'payment_processor_id' => 0,
1615 'contact_id' => $this->_individualId,
1616 'contribution_id' => $contribution['id'],
1617 ]);
1618 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]);
1619 $contribution = $this->callAPISuccess('Contribution', 'get', [
1620 'contact_id' => $this->_individualId,
1621 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
1622 ]);
1623 $this->assertEquals($endDate, $membership['end_date'], 'Membership end date should be same (' . $endDate . ') after payment');
1624 $this->assertCount(1, $contribution['values'], 'Completed contribution should be fetched.');
1625 }
1626
1627 /**
1628 * Test Membership Payment owned by other contact, membership view should show all contribution records in listing.
1629 * is other contact.
1630 *
1631 * @throws \CRM_Core_Exception
1632 * @throws \CiviCRM_API3_Exception
1633 * @throws \Exception
1634 */
1635 public function testMembershipViewContributionOwnerDifferent() {
1636 // Membership Owner
1637 $contactId1 = $this->individualCreate();
1638
1639 // Contribution Onwer
1640 $contactId2 = $this->individualCreate();
1641
1642 // create new membership type
1643 $membershipTypeAnnualFixed = $this->callAPISuccess('MembershipType', 'create', [
1644 'domain_id' => 1,
1645 'name' => 'AnnualFixed 2',
1646 'member_of_contact_id' => $this->organizationCreate(),
1647 'duration_unit' => 'year',
1648 'minimum_fee' => 50,
1649 'duration_interval' => 1,
1650 'period_type' => 'fixed',
1651 'fixed_period_start_day' => '101',
1652 'fixed_period_rollover_day' => '1231',
1653 'financial_type_id' => 2,
1654 ]);
1655
1656 // create Membership
1657 $membershipId = $this->contactMembershipCreate([
1658 'contact_id' => $contactId1,
1659 'membership_type_id' => $membershipTypeAnnualFixed['id'],
1660 'status_id' => 'New',
1661 ]);
1662
1663 // 1st Payment
1664 $contriParams = [
1665 'membership_id' => $membershipId,
1666 'total_amount' => 25,
1667 'financial_type_id' => 2,
1668 'contact_id' => $contactId2,
1669 'receive_date' => '2020-08-08',
1670 ];
1671 $contribution1 = CRM_Member_BAO_Membership::recordMembershipContribution($contriParams);
1672
1673 // 2nd Payment
1674 $contriParams = [
1675 'membership_id' => $membershipId,
1676 'total_amount' => 25,
1677 'financial_type_id' => 2,
1678 'contact_id' => $contactId2,
1679 'receive_date' => '2020-07-08',
1680 ];
1681 $contribution2 = CRM_Member_BAO_Membership::recordMembershipContribution($contriParams);
1682
1683 // View Membership record
1684 $membershipViewForm = new CRM_Member_Form_MembershipView();
1685 $membershipViewForm->controller = new CRM_Core_Controller_Simple('CRM_Member_Form_MembershipView', 'View Membership');
1686 $membershipViewForm->set('id', $membershipId);
1687 $membershipViewForm->set('context', 'membership');
1688 $membershipViewForm->controller->setEmbedded(TRUE);
1689 $membershipViewForm->preProcess();
1690
1691 // get contribution rows related to membership payments
1692 $templateVar = $membershipViewForm::getTemplate()->get_template_vars('rows');
1693
1694 $this->assertEquals($templateVar[0]['contribution_id'], $contribution1->id);
1695 $this->assertEquals($templateVar[0]['contact_id'], $contactId2);
1696
1697 $this->assertEquals($templateVar[1]['contribution_id'], $contribution2->id);
1698 $this->assertEquals($templateVar[1]['contact_id'], $contactId2);
1699 $this->assertEquals(count($templateVar), 2);
1700 }
1701
1702 }