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