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