Minor fixes
[civicrm-core.git] / tests / phpunit / CRM / Member / Form / MembershipTest.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2017 |
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 */
27
28 /**
29 * File for the MembershipTest class
30 *
31 * (PHP 5)
32 *
33 * @author Walt Haas <walt@dharmatech.org> (801) 534-1262
34 */
35
36 /**
37 * Test CRM_Member_Form_Membership functions.
38 *
39 * @package CiviCRM
40 * @group headless
41 */
42 class CRM_Member_Form_MembershipTest extends CiviUnitTestCase {
43
44 /**
45 * Assume empty database with just civicrm_data.
46 */
47 protected $_individualId;
48 protected $_contribution;
49 protected $_financialTypeId = 1;
50 protected $_apiversion;
51 protected $_entity = 'Membership';
52 protected $_params;
53 protected $_ids = array();
54 protected $_paymentProcessorID;
55
56 /**
57 * Membership type ID for annual fixed membership.
58 *
59 * @var int
60 */
61 protected $membershipTypeAnnualFixedID;
62
63 /**
64 * Parameters to create payment processor.
65 *
66 * @var array
67 */
68 protected $_processorParams = array();
69
70 /**
71 * ID of created membership.
72 *
73 * @var int
74 */
75 protected $_membershipID;
76
77 /**
78 * Payment instrument mapping.
79 *
80 * @var array
81 */
82 protected $paymentInstruments = array();
83
84 /**
85 * @var CiviMailUtils
86 */
87 protected $mut;
88
89 /**
90 * Test setup for every test.
91 *
92 * Connect to the database, truncate the tables that will be used
93 * and redirect stdin to a temporary file.
94 */
95 public function setUp() {
96 $this->_apiversion = 3;
97 parent::setUp();
98
99 $this->_individualId = $this->individualCreate();
100 $this->_paymentProcessorID = $this->processorCreate();
101 // Insert test data.
102 $op = new PHPUnit_Extensions_Database_Operation_Insert();
103 $op->execute($this->_dbconn,
104 $this->createFlatXMLDataSet(
105 dirname(__FILE__) . '/dataset/data.xml'
106 )
107 );
108 $membershipTypeAnnualFixed = $this->callAPISuccess('membership_type', 'create', array(
109 'domain_id' => 1,
110 'name' => "AnnualFixed",
111 'member_of_contact_id' => 23,
112 'duration_unit' => "year",
113 'minimum_fee' => 50,
114 'duration_interval' => 1,
115 'period_type' => "fixed",
116 'fixed_period_start_day' => "101",
117 'fixed_period_rollover_day' => "1231",
118 'relationship_type_id' => 20,
119 'financial_type_id' => 2,
120 ));
121 $this->membershipTypeAnnualFixedID = $membershipTypeAnnualFixed['id'];
122
123 $instruments = $this->callAPISuccess('contribution', 'getoptions', array('field' => 'payment_instrument_id'));
124 $this->paymentInstruments = $instruments['values'];
125 }
126
127 /**
128 * Clean up after each test.
129 */
130 public function tearDown() {
131 $this->quickCleanUpFinancialEntities();
132 $this->quickCleanup(
133 array(
134 'civicrm_relationship',
135 'civicrm_membership_type',
136 'civicrm_membership',
137 'civicrm_uf_match',
138 )
139 );
140 foreach (array(17, 18, 23, 32) as $contactID) {
141 $this->callAPISuccess('contact', 'delete', array('id' => $contactID, 'skip_undelete' => TRUE));
142 }
143 $this->callAPISuccess('relationship_type', 'delete', array('id' => 20));
144 }
145
146 /**
147 * Test CRM_Member_Form_Membership::buildQuickForm()
148 */
149 //function testCRMMemberFormMembershipBuildQuickForm()
150 //{
151 // throw new PHPUnit_Framework_IncompleteTestError( "not implemented" );
152 //}
153
154 /**
155 * Test CRM_Member_Form_Membership::formRule() with a parameter
156 * that has an empty contact_select_id value
157 */
158 public function testFormRuleEmptyContact() {
159 $params = array(
160 'contact_select_id' => 0,
161 'membership_type_id' => array(1 => NULL),
162 );
163 $files = array();
164 $obj = new CRM_Member_Form_Membership();
165 $rc = $obj->formRule($params, $files, $obj);
166 $this->assertType('array', $rc);
167 $this->assertTrue(array_key_exists('membership_type_id', $rc));
168
169 $params['membership_type_id'] = array(1 => 3);
170 $rc = $obj->formRule($params, $files, $obj);
171 $this->assertType('array', $rc);
172 $this->assertTrue(array_key_exists('join_date', $rc));
173 }
174
175 /**
176 * Test that form rule fails if start date is before join date.
177 *
178 * Test CRM_Member_Form_Membership::formRule() with a parameter
179 * that has an start date before the join date and a rolling
180 * membership type.
181 */
182 public function testFormRuleRollingEarlyStart() {
183 $unixNow = time();
184 $ymdNow = date('m/d/Y', $unixNow);
185 $unixYesterday = $unixNow - (24 * 60 * 60);
186 $ymdYesterday = date('m/d/Y', $unixYesterday);
187 $params = array(
188 'join_date' => $ymdNow,
189 'start_date' => $ymdYesterday,
190 'end_date' => '',
191 'membership_type_id' => array('23', '15'),
192 );
193 $files = array();
194 $obj = new CRM_Member_Form_Membership();
195 $rc = call_user_func(array('CRM_Member_Form_Membership', 'formRule'),
196 $params, $files, $obj
197 );
198 $this->assertType('array', $rc);
199 $this->assertTrue(array_key_exists('start_date', $rc));
200 }
201
202 /**
203 * Test CRM_Member_Form_Membership::formRule() with a parameter
204 * that has an end date before the start date and a rolling
205 * membership type
206 */
207 public function testFormRuleRollingEarlyEnd() {
208 $unixNow = time();
209 $ymdNow = date('m/d/Y', $unixNow);
210 $unixYesterday = $unixNow - (24 * 60 * 60);
211 $ymdYesterday = date('m/d/Y', $unixYesterday);
212 $params = array(
213 'join_date' => $ymdNow,
214 'start_date' => $ymdNow,
215 'end_date' => $ymdYesterday,
216 'membership_type_id' => array('23', '15'),
217 );
218 $files = array();
219 $obj = new CRM_Member_Form_Membership();
220 $rc = $obj->formRule($params, $files, $obj);
221 $this->assertType('array', $rc);
222 $this->assertTrue(array_key_exists('end_date', $rc));
223 }
224
225 /**
226 * Test CRM_Member_Form_Membership::formRule() with end date but no start date and a rolling membership type.
227 */
228 public function testFormRuleRollingEndNoStart() {
229 $unixNow = time();
230 $ymdNow = date('m/d/Y', $unixNow);
231 $unixYearFromNow = $unixNow + (365 * 24 * 60 * 60);
232 $ymdYearFromNow = date('m/d/Y', $unixYearFromNow);
233 $params = array(
234 'join_date' => $ymdNow,
235 'start_date' => '',
236 'end_date' => $ymdYearFromNow,
237 'membership_type_id' => array('23', '15'),
238 );
239 $files = array();
240 $obj = new CRM_Member_Form_Membership();
241 $rc = $obj->formRule($params, $files, $obj);
242 $this->assertType('array', $rc);
243 $this->assertTrue(array_key_exists('start_date', $rc));
244 }
245
246 /**
247 * Test CRM_Member_Form_Membership::formRule() with a parameter
248 * that has an end date and a lifetime membership type
249 */
250 public function testFormRuleRollingLifetimeEnd() {
251 $unixNow = time();
252 $unixYearFromNow = $unixNow + (365 * 24 * 60 * 60);
253 $params = array(
254 'join_date' => date('m/d/Y', $unixNow),
255 'start_date' => date('m/d/Y', $unixNow),
256 'end_date' => date('m/d/Y',
257 $unixYearFromNow
258 ),
259 'membership_type_id' => array('23', '25'),
260 );
261 $files = array();
262 $obj = new CRM_Member_Form_Membership();
263 $rc = $obj->formRule($params, $files, $obj);
264 $this->assertType('array', $rc);
265 $this->assertTrue(array_key_exists('status_id', $rc));
266 }
267
268 /**
269 * Test CRM_Member_Form_Membership::formRule() with a parameter
270 * that has an override and no status
271 */
272 public function testFormRuleOverrideNoStatus() {
273 $unixNow = time();
274 $params = array(
275 'join_date' => date('m/d/Y', $unixNow),
276 'membership_type_id' => array('23', '25'),
277 'is_override' => TRUE,
278 );
279 $files = array();
280 $obj = new CRM_Member_Form_Membership();
281 $rc = $obj->formRule($params, $files, $obj);
282 $this->assertType('array', $rc);
283 $this->assertTrue(array_key_exists('status_id', $rc));
284 }
285
286 /**
287 * Test CRM_Member_Form_Membership::formRule() with a join date
288 * of one month from now and a rolling membership type
289 */
290 public function testFormRuleRollingJoin1MonthFromNow() {
291 $unixNow = time();
292 $unix1MFmNow = $unixNow + (31 * 24 * 60 * 60);
293 $params = array(
294 'join_date' => date('m/d/Y', $unix1MFmNow),
295 'start_date' => '',
296 'end_date' => '',
297 'membership_type_id' => array('23', '15'),
298 );
299 $files = array();
300 $obj = new CRM_Member_Form_Membership();
301 $rc = $obj->formRule($params, $files, $obj);
302
303 // Should have found no valid membership status.
304 $this->assertType('array', $rc);
305 $this->assertTrue(array_key_exists('_qf_default', $rc));
306 }
307
308 /**
309 * Test CRM_Member_Form_Membership::formRule() with a join date of today and a rolling membership type.
310 */
311 public function testFormRuleRollingJoinToday() {
312 $unixNow = time();
313 $params = array(
314 'join_date' => date('m/d/Y', $unixNow),
315 'start_date' => '',
316 'end_date' => '',
317 'membership_type_id' => array('23', '15'),
318 );
319 $files = array();
320 $obj = new CRM_Member_Form_Membership();
321 $rc = $obj->formRule($params, $files, $obj);
322
323 // Should have found New membership status
324 $this->assertTrue($rc);
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 */
331 public function testFormRuleRollingJoin1MonthAgo() {
332 $unixNow = time();
333 $unix1MAgo = $unixNow - (31 * 24 * 60 * 60);
334 $params = array(
335 'join_date' => date('m/d/Y', $unix1MAgo),
336 'start_date' => '',
337 'end_date' => '',
338 'membership_type_id' => array('23', '15'),
339 );
340 $files = array();
341 $obj = new CRM_Member_Form_Membership();
342 $rc = $obj->formRule($params, $files, $obj);
343
344 // Should have found New membership status.
345 $this->assertTrue($rc);
346 }
347
348 /**
349 * Test CRM_Member_Form_Membership::formRule() with a join date of six months ago and a rolling membership type.
350 */
351 public function testFormRuleRollingJoin6MonthsAgo() {
352 $unixNow = time();
353 $unix6MAgo = $unixNow - (180 * 24 * 60 * 60);
354 $params = array(
355 'join_date' => date('m/d/Y', $unix6MAgo),
356 'start_date' => '',
357 'end_date' => '',
358 'membership_type_id' => array('23', '15'),
359 );
360 $files = array();
361 $obj = new CRM_Member_Form_Membership();
362 $rc = $obj->formRule($params, $files, $obj);
363
364 // Should have found Current membership status.
365 $this->assertTrue($rc);
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 */
372 public function testFormRuleRollingJoin1YearAgo() {
373 $unixNow = time();
374 $unix1YAgo = $unixNow - (370 * 24 * 60 * 60);
375 $params = array(
376 'join_date' => date('m/d/Y', $unix1YAgo),
377 'start_date' => '',
378 'end_date' => '',
379 'membership_type_id' => array('23', '15'),
380 );
381 $files = array();
382 $obj = new CRM_Member_Form_Membership();
383 $rc = $obj->formRule($params, $files, $obj);
384
385 // Should have found Grace membership status
386 $this->assertTrue($rc);
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 */
393 public function testFormRuleRollingJoin2YearsAgo() {
394 $unixNow = time();
395 $unix2YAgo = $unixNow - (2 * 365 * 24 * 60 * 60);
396 $params = array(
397 'join_date' => date('m/d/Y', $unix2YAgo),
398 'start_date' => '',
399 'end_date' => '',
400 'membership_type_id' => array('23', '15'),
401 );
402 $files = array();
403 $obj = new CRM_Member_Form_Membership();
404 $rc = $obj->formRule($params, $files, $obj);
405
406 // Should have found Expired membership status
407 $this->assertTrue($rc);
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 */
414 public function testFormRuleFixedJoin6MonthsAgo() {
415 $unixNow = time();
416 $unix6MAgo = $unixNow - (180 * 24 * 60 * 60);
417 $params = array(
418 'join_date' => date('m/d/Y', $unix6MAgo),
419 'start_date' => '',
420 'end_date' => '',
421 'membership_type_id' => array('23', '7'),
422 );
423 $files = array();
424 $obj = new CRM_Member_Form_Membership();
425 $rc = $obj->formRule($params, $files, $obj);
426
427 // Should have found Current membership status
428 $this->assertTrue($rc);
429 }
430
431 /**
432 * Test the submit function of the membership form.
433 */
434 public function testSubmit() {
435 $form = $this->getForm();
436 $form->preProcess();
437 $this->mut = new CiviMailUtils($this, TRUE);
438 $form->_mode = 'test';
439 $this->createLoggedInUser();
440 $params = array(
441 'cid' => $this->_individualId,
442 'join_date' => date('m/d/Y', time()),
443 'start_date' => '',
444 'end_date' => '',
445 // This format reflects the 23 being the organisation & the 25 being the type.
446 'membership_type_id' => array(23, $this->membershipTypeAnnualFixedID),
447 'auto_renew' => '0',
448 'max_related' => '',
449 'num_terms' => '1',
450 'source' => '',
451 'total_amount' => '50.00',
452 'financial_type_id' => '2', //Member dues, see data.xml
453 'soft_credit_type_id' => '',
454 'soft_credit_contact_id' => '',
455 'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
456 'payment_processor_id' => $this->_paymentProcessorID,
457 'credit_card_number' => '4111111111111111',
458 'cvv2' => '123',
459 'credit_card_exp_date' => array(
460 'M' => '9',
461 'Y' => '2024', // TODO: Future proof
462 ),
463 'credit_card_type' => 'Visa',
464 'billing_first_name' => 'Test',
465 'billing_middlename' => 'Last',
466 'billing_street_address-5' => '10 Test St',
467 'billing_city-5' => 'Test',
468 'billing_state_province_id-5' => '1003',
469 'billing_postal_code-5' => '90210',
470 'billing_country_id-5' => '1228',
471 'send_receipt' => TRUE,
472 'receipt_text' => 'Receipt text',
473 );
474 $form->_contactID = $this->_individualId;
475 $form->testSubmit($params);
476 $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
477 $this->callAPISuccessGetCount('ContributionRecur', array('contact_id' => $this->_individualId), 0);
478 $contribution = $this->callAPISuccess('Contribution', 'get', array(
479 'contact_id' => $this->_individualId,
480 'is_test' => TRUE,
481 ));
482
483 //CRM-20264 : Check that CC type and number (last 4 digit) is stored during backoffice membership payment
484 $lastFinancialTrxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($contribution['id'], 'DESC');
485 $financialTrxn = $this->callAPISuccessGetSingle(
486 'FinancialTrxn',
487 array(
488 'id' => $lastFinancialTrxnId['financialTrxnId'],
489 'return' => array('card_type_id', 'pan_truncation'),
490 )
491 );
492 $this->assertEquals(1, $financialTrxn['card_type_id']);
493 $this->assertEquals(1111, $financialTrxn['pan_truncation']);
494
495 $this->callAPISuccessGetCount('LineItem', array(
496 'entity_id' => $membership['id'],
497 'entity_table' => 'civicrm_membership',
498 'contribution_id' => $contribution['id'],
499 ), 1);
500
501 $this->_checkFinancialRecords(array(
502 'id' => $contribution['id'],
503 'total_amount' => 50,
504 'financial_account_id' => 2,
505 'payment_instrument_id' => $this->callAPISuccessGetValue('PaymentProcessor', array(
506 'id' => $this->_paymentProcessorID,
507 'return' => 'payment_instrument_id',
508 )),
509 ), 'online');
510 $this->mut->checkMailLog(array(
511 '50',
512 'Receipt text',
513 ));
514 $this->mut->stop();
515 }
516
517 /**
518 * Test the submit function of the membership form on membership type change.
519 * Check if the related contribuion is also updated if the minimum_fee didn't match
520 */
521 public function testContributionUpdateOnMembershipTypeChange() {
522 // Step 1: Create a Membership via backoffice whose with 50.00 payment
523 $form = $this->getForm();
524 $form->preProcess();
525 $this->mut = new CiviMailUtils($this, TRUE);
526 $this->createLoggedInUser();
527 $priceSet = $this->callAPISuccess('PriceSet', 'Get', array("extends" => "CiviMember"));
528 $form->set('priceSetId', $priceSet['id']);
529 CRM_Price_BAO_PriceSet::buildPriceSet($form);
530 $params = array(
531 'cid' => $this->_individualId,
532 'join_date' => date('m/d/Y', time()),
533 'start_date' => '',
534 'end_date' => '',
535 // This format reflects the 23 being the organisation & the 25 being the type.
536 'membership_type_id' => array(23, $this->membershipTypeAnnualFixedID),
537 'record_contribution' => 1,
538 'total_amount' => 50,
539 'receive_date' => date('m/d/Y', time()),
540 'receive_date_time' => '08:36PM',
541 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
542 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
543 'financial_type_id' => '2', //Member dues, see data.xml
544 'payment_processor_id' => $this->_paymentProcessorID,
545 );
546 $form->_contactID = $this->_individualId;
547 $form->testSubmit($params);
548 $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
549 // check the membership status after partial payment, if its Pending
550 $this->assertEquals(array_search('New', CRM_Member_PseudoConstant::membershipStatus()), $membership['status_id']);
551 $contribution = $this->callAPISuccessGetSingle('Contribution', array(
552 'contact_id' => $this->_individualId,
553 ));
554 $this->assertEquals('Completed', $contribution['contribution_status']);
555 $this->assertEquals(50.00, $contribution['total_amount']);
556 $this->assertEquals(50.00, $contribution['net_amount']);
557
558 // Step 2: Change the membership type whose minimum free is less than earlier membership
559 $secondMembershipType = $this->callAPISuccess('membership_type', 'create', array(
560 'domain_id' => 1,
561 'name' => "Second Test Membership",
562 'member_of_contact_id' => 23,
563 'duration_unit' => "month",
564 'minimum_fee' => 25,
565 'duration_interval' => 1,
566 'period_type' => "fixed",
567 'fixed_period_start_day' => "101",
568 'fixed_period_rollover_day' => "1231",
569 'relationship_type_id' => 20,
570 'financial_type_id' => 2,
571 ));
572 Civi::settings()->set('update_contribution_on_membership_type_change', TRUE);
573 $form = $this->getForm();
574 $form->preProcess();
575 $form->_id = $membership['id'];
576 $form->set('priceSetId', $priceSet['id']);
577 CRM_Price_BAO_PriceSet::buildPriceSet($form);
578 $form->_action = CRM_Core_Action::UPDATE;
579 $params = array(
580 'cid' => $this->_individualId,
581 'join_date' => date('m/d/Y', time()),
582 'start_date' => '',
583 'end_date' => '',
584 // This format reflects the 23 being the organisation & the 25 being the type.
585 'membership_type_id' => array(23, $secondMembershipType['id']),
586 'record_contribution' => 1,
587 'status_id' => 1,
588 'total_amount' => 25,
589 'receive_date' => date('m/d/Y', time()),
590 'receive_date_time' => '08:36PM',
591 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
592 'financial_type_id' => '2', //Member dues, see data.xml
593 'payment_processor_id' => $this->_paymentProcessorID,
594 );
595 $form->_contactID = $this->_individualId;
596 $form->testSubmit($params);
597 $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
598 // check the membership status after partial payment, if its Pending
599 $contribution = $this->callAPISuccessGetSingle('Contribution', array(
600 'contact_id' => $this->_individualId,
601 ));
602 $payment = CRM_Contribute_BAO_Contribution::getPaymentInfo($membership['id'], 'membership', FALSE, TRUE);
603 // Check the contribution status on membership type change whose minimum fee was less than earlier memebership
604 $this->assertEquals('Pending refund', $contribution['contribution_status']);
605 // Earlier paid amount
606 $this->assertEquals(50, $payment['paid']);
607 // balance remaning
608 $this->assertEquals(-25, $payment['balance']);
609 }
610
611 /**
612 * Test the submit function of the membership form.
613 */
614 public function testSubmitRecur() {
615 $form = $this->getForm();
616
617 $this->callAPISuccess('MembershipType', 'create', array(
618 'id' => $this->membershipTypeAnnualFixedID,
619 'duration_unit' => 'month',
620 'duration_interval' => 1,
621 'auto_renew' => TRUE,
622 ));
623 $form->preProcess();
624 $this->createLoggedInUser();
625 $params = $this->getBaseSubmitParams();
626 $form->_mode = 'test';
627 $form->_contactID = $this->_individualId;
628 $form->testSubmit($params);
629 $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
630 $this->callAPISuccessGetCount('ContributionRecur', array('contact_id' => $this->_individualId), 1);
631
632 $contribution = $this->callAPISuccess('Contribution', 'get', array(
633 'contact_id' => $this->_individualId,
634 'is_test' => TRUE,
635 ));
636
637 // CRM-16992.
638 $this->callAPISuccessGetCount('LineItem', array(
639 'entity_id' => $membership['id'],
640 'entity_table' => 'civicrm_membership',
641 'contribution_id' => $contribution['id'],
642 ), 1);
643 }
644
645 /**
646 * Test membership form with Failed Contribution.
647 */
648 public function testFormStatusUpdate() {
649 $form = $this->getForm();
650 $form->preProcess();
651 $this->_individualId = $this->createLoggedInUser();
652 $memParams = array(
653 'contact_id' => $this->_individualId,
654 'membership_type_id' => $this->membershipTypeAnnualFixedID,
655 'is_override' => TRUE,
656 'status_id' => array_search('Cancelled', CRM_Member_PseudoConstant::membershipStatus()),
657 );
658 $params = $this->getBaseSubmitParams();
659 $params['id'] = $this->contactMembershipCreate($memParams);
660 unset($params['price_set_id']);
661 unset($params['credit_card_number']);
662 unset($params['cvv2']);
663 unset($params['credit_card_exp_date']);
664 unset($params['credit_card_type']);
665 unset($params['send_receipt']);
666 unset($params['is_recur']);
667
668 // process date params to mysql date format.
669 $dateTypes = array(
670 'join_date' => 'joinDate',
671 'start_date' => 'startDate',
672 'end_date' => 'endDate',
673 );
674 $previousStatus = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership', $params['id'], 'status_id');
675 foreach ($dateTypes as $dateField => $dateVariable) {
676 $params[$dateField] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership', $params['id'], $dateField);
677 }
678 $form->_id = $params['id'];
679 $form->_mode = NULL;
680 $form->_contactID = $this->_individualId;
681
682 $form->testSubmit($params);
683 $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
684 //Assert the status remains when the form dates are not modified.
685 $this->assertEquals($membership['status_id'], $previousStatus);
686 }
687
688 /**
689 * CRM-20946: Test the financial entires especially the reversed amount,
690 * after related Contribution is cancelled
691 */
692 public function testFinancialEntiriesOnCancelledContribution() {
693 $form = $this->getForm(NULL);
694 $form->preProcess();
695 $this->createLoggedInUser();
696
697 // create a price-set of price-field of type checkbox and each price-option corrosponds to a membership type
698 $priceSet = $this->callAPISuccess('price_set', 'create', array(
699 'is_quick_config' => 0,
700 'extends' => 'CiviMember',
701 'financial_type_id' => 1,
702 'title' => 'my Page',
703 ));
704 $priceSetID = $priceSet['id'];
705 // create respective checkbox price-field
706 $priceField = $this->callAPISuccess('price_field', 'create', array(
707 'price_set_id' => $priceSetID,
708 'label' => 'Memberships',
709 'html_type' => 'Checkbox',
710 ));
711 $priceFieldID = $priceField['id'];
712 // create two price options, each represent a membership type of amount 20 and 10 respectively
713 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
714 'price_set_id' => $priceSetID,
715 'price_field_id' => $priceField['id'],
716 'label' => 'Long Haired Goat',
717 'amount' => 20,
718 'financial_type_id' => 'Donation',
719 'membership_type_id' => 15,
720 'membership_num_terms' => 1,
721 )
722 );
723 $pfvIDs = array($priceFieldValue['id'] => 1);
724 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
725 'price_set_id' => $priceSetID,
726 'price_field_id' => $priceField['id'],
727 'label' => 'Shoe-eating Goat',
728 'amount' => 10,
729 'financial_type_id' => 'Donation',
730 'membership_type_id' => 35,
731 'membership_num_terms' => 2,
732 )
733 );
734 $pfvIDs[$priceFieldValue['id']] = 1;
735
736 // register for both of this memberships via backoffice membership form submission
737 $params = array(
738 'cid' => $this->_individualId,
739 'join_date' => date('m/d/Y', time()),
740 'start_date' => '',
741 'end_date' => '',
742 // This format reflects the 23 being the organisation & the 25 being the type.
743 "price_$priceFieldID" => $pfvIDs,
744 "price_set_id" => $priceSetID,
745 'membership_type_id' => array(1 => 0),
746 'auto_renew' => '0',
747 'max_related' => '',
748 'num_terms' => '2',
749 'source' => '',
750 'total_amount' => '30.00',
751 //Member dues, see data.xml
752 'financial_type_id' => '2',
753 'soft_credit_type_id' => '',
754 'soft_credit_contact_id' => '',
755 'payment_instrument_id' => 4,
756 'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
757 'receipt_text_signup' => 'Thank you text',
758 'payment_processor_id' => $this->_paymentProcessorID,
759 'record_contribution' => TRUE,
760 'trxn_id' => 777,
761 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_DAO_Contribution', 'contribution_status_id', 'Pending'),
762 'billing_first_name' => 'Test',
763 'billing_middlename' => 'Last',
764 'billing_street_address-5' => '10 Test St',
765 'billing_city-5' => 'Test',
766 'billing_state_province_id-5' => '1003',
767 'billing_postal_code-5' => '90210',
768 'billing_country_id-5' => '1228',
769 );
770 $form->testSubmit($params);
771
772 // cancel the related contribution via API
773 $contribution = $this->callAPISuccessGetSingle('Contribution', array(
774 'contact_id' => $this->_individualId,
775 'contribution_status_id' => 2,
776 ));
777 $this->callAPISuccess('Contribution', 'create', array(
778 'id' => $contribution['id'],
779 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_DAO_Contribution', 'contribution_status_id', 'Cancelled'),
780 ));
781
782 // fetch financial_trxn ID of the related contribution
783 $sql = "SELECT financial_trxn_id
784 FROM civicrm_entity_financial_trxn
785 WHERE entity_id = %1 AND entity_table = 'civicrm_contribution'
786 ORDER BY id DESC
787 LIMIT 1
788 ";
789 $financialTrxnID = CRM_Core_DAO::singleValueQuery($sql, array(1 => array($contribution['id'], 'Int')));
790
791 // fetch entity_financial_trxn records and compare their cancelled records
792 $result = $this->callAPISuccess('EntityFinancialTrxn', 'Get', array(
793 'financial_trxn_id' => $financialTrxnID,
794 'entity_table' => 'civicrm_financial_item',
795 ));
796 // compare the reversed amounts of respective memberships after cancelling contribution
797 $cancelledMembershipAmounts = array(
798 -20.00,
799 -10.00,
800 );
801 $count = 0;
802 foreach ($result['values'] as $record) {
803 $this->assertEquals($cancelledMembershipAmounts[$count], $record['amount']);
804 $count++;
805 }
806 }
807
808 /**
809 * Test the submit function of the membership form.
810 */
811 public function testSubmitPayLaterWithBilling() {
812 $form = $this->getForm(NULL);
813 $form->preProcess();
814 $this->createLoggedInUser();
815 $params = array(
816 'cid' => $this->_individualId,
817 'join_date' => date('m/d/Y', time()),
818 'start_date' => '',
819 'end_date' => '',
820 // This format reflects the 23 being the organisation & the 25 being the type.
821 'membership_type_id' => array(23, $this->membershipTypeAnnualFixedID),
822 'auto_renew' => '0',
823 'max_related' => '',
824 'num_terms' => '2',
825 'source' => '',
826 'total_amount' => '50.00',
827 //Member dues, see data.xml
828 'financial_type_id' => '2',
829 'soft_credit_type_id' => '',
830 'soft_credit_contact_id' => '',
831 'payment_instrument_id' => 4,
832 'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
833 'receipt_text_signup' => 'Thank you text',
834 'payment_processor_id' => $this->_paymentProcessorID,
835 'record_contribution' => TRUE,
836 'trxn_id' => 777,
837 'contribution_status_id' => 2,
838 'billing_first_name' => 'Test',
839 'billing_middlename' => 'Last',
840 'billing_street_address-5' => '10 Test St',
841 'billing_city-5' => 'Test',
842 'billing_state_province_id-5' => '1003',
843 'billing_postal_code-5' => '90210',
844 'billing_country_id-5' => '1228',
845 );
846 $form->_contactID = $this->_individualId;
847
848 $form->testSubmit($params);
849 $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
850 $contribution = $this->callAPISuccessGetSingle('Contribution', array(
851 'contact_id' => $this->_individualId,
852 'contribution_status_id' => 2,
853 ));
854 $this->assertEquals($contribution['trxn_id'], 777);
855
856 $this->callAPISuccessGetCount('LineItem', array(
857 'entity_id' => $membership['id'],
858 'entity_table' => 'civicrm_membership',
859 'contribution_id' => $contribution['id'],
860 ), 1);
861 $this->callAPISuccessGetSingle('address', array(
862 'contact_id' => $this->_individualId,
863 'street_address' => '10 Test St',
864 'postal_code' => 90210,
865 ));
866 }
867
868 /**
869 * Test if membership is updated to New after contribution
870 * is updated from Partially paid to Completed.
871 */
872 public function testSubmitUpdateMembershipFromPartiallyPaid() {
873 $memStatus = CRM_Member_BAO_Membership::buildOptions('status_id', 'validate');
874
875 //Perform a pay later membership contribution.
876 $this->testSubmitPayLaterWithBilling();
877 $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
878 $this->assertEquals($membership['status_id'], array_search('Pending', $memStatus));
879 $contribution = $this->callAPISuccessGetSingle('MembershipPayment', array(
880 'membership_id' => $membership['id'],
881 ));
882
883 //Update contribution to Partially paid.
884 $prevContribution = $this->callAPISuccess('Contribution', 'create', array(
885 'id' => $contribution['contribution_id'],
886 'contribution_status_id' => 'Partially paid',
887 ));
888 $prevContribution = $prevContribution['values'][1];
889
890 //Complete the contribution from offline form.
891 $form = new CRM_Contribute_Form_Contribution();
892 $submitParams = array(
893 'id' => $contribution['contribution_id'],
894 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
895 'price_set_id' => 0,
896 );
897 $fields = array('total_amount', 'net_amount', 'financial_type_id', 'receive_date', 'contact_id', 'payment_instrument_id');
898 foreach ($fields as $val) {
899 $submitParams[$val] = $prevContribution[$val];
900 }
901 $form->testSubmit($submitParams, CRM_Core_Action::UPDATE);
902
903 //Check if Membership is updated to New.
904 $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
905 $this->assertEquals($membership['status_id'], array_search('New', $memStatus));
906 }
907
908 /**
909 * Test the submit function of the membership form.
910 */
911 public function testSubmitRecurCompleteInstant() {
912 $form = $this->getForm();
913 $mut = new CiviMailUtils($this, TRUE);
914 $processor = Civi\Payment\System::singleton()->getById($this->_paymentProcessorID);
915 $processor->setDoDirectPaymentResult(array(
916 'payment_status_id' => 1,
917 'trxn_id' => 'kettles boil water',
918 'fee_amount' => .14,
919 ));
920 $processorDetail = $processor->getPaymentProcessor();
921 $this->callAPISuccess('MembershipType', 'create', array(
922 'id' => $this->membershipTypeAnnualFixedID,
923 'duration_unit' => 'month',
924 'duration_interval' => 1,
925 'auto_renew' => TRUE,
926 ));
927 $form->preProcess();
928 $this->createLoggedInUser();
929 $params = $this->getBaseSubmitParams();
930 $form->_mode = 'test';
931 $form->_contactID = $this->_individualId;
932 $form->testSubmit($params);
933 $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
934 $this->callAPISuccessGetCount('ContributionRecur', array('contact_id' => $this->_individualId), 1);
935
936 $contribution = $this->callAPISuccess('Contribution', 'getsingle', array(
937 'contact_id' => $this->_individualId,
938 'is_test' => TRUE,
939 ));
940
941 $this->assertEquals(.14, $contribution['fee_amount']);
942 $this->assertEquals('kettles boil water', $contribution['trxn_id']);
943 $this->assertEquals($processorDetail['payment_instrument_id'], $contribution['payment_instrument_id']);
944
945 $this->callAPISuccessGetCount('LineItem', array(
946 'entity_id' => $membership['id'],
947 'entity_table' => 'civicrm_membership',
948 'contribution_id' => $contribution['id'],
949 ), 1);
950 $mut->checkMailLog(array(
951 '===========================================================
952 Billing Name and Address
953 ===========================================================
954 Test
955 10 Test St
956 Test, AR 90210
957 US',
958 '===========================================================
959 Membership Information
960 ===========================================================
961 Membership Type: AnnualFixed
962 Membership Start Date: ',
963 '===========================================================
964 Credit Card Information
965 ===========================================================
966 Visa
967 ************1111
968 Expires: ',
969 )
970 );
971 $mut->stop();
972
973 }
974
975 /**
976 * Test membership form with Failed Contribution.
977 */
978 public function testFormWithFailedContribution() {
979 $form = $this->getForm();
980 $form->preProcess();
981 $this->createLoggedInUser();
982 $params = $this->getBaseSubmitParams();
983 unset($params['price_set_id']);
984 unset($params['credit_card_number']);
985 unset($params['cvv2']);
986 unset($params['credit_card_exp_date']);
987 unset($params['credit_card_type']);
988 unset($params['send_receipt']);
989 unset($params['is_recur']);
990
991 $params['record_contribution'] = TRUE;
992 $params['contribution_status_id'] = array_search('Failed', CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'));
993 $form->_mode = NULL;
994 $form->_contactID = $this->_individualId;
995
996 $form->testSubmit($params);
997 $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
998 $form->testSubmit($params);
999 $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
1000 $this->assertEquals($membership['status_id'], array_search('Pending', CRM_Member_PseudoConstant::membershipStatus()));
1001 }
1002
1003 /**
1004 * Get a membership form object.
1005 *
1006 * We need to instantiate the form to run preprocess, which means we have to trick it about the request method.
1007 *
1008 * @return \CRM_Member_Form_Membership
1009 */
1010 protected function getForm() {
1011 $form = new CRM_Member_Form_Membership();
1012 $_SERVER['REQUEST_METHOD'] = 'GET';
1013 $form->controller = new CRM_Core_Controller();
1014 return $form;
1015 }
1016
1017 /**
1018 * @return array
1019 */
1020 protected function getBaseSubmitParams() {
1021 $params = array(
1022 'cid' => $this->_individualId,
1023 'price_set_id' => 0,
1024 'join_date' => date('m/d/Y', time()),
1025 'start_date' => '',
1026 'end_date' => '',
1027 'campaign_id' => '',
1028 // This format reflects the 23 being the organisation & the 25 being the type.
1029 'membership_type_id' => array(23, $this->membershipTypeAnnualFixedID),
1030 'auto_renew' => '1',
1031 'is_recur' => 1,
1032 'max_related' => 0,
1033 'num_terms' => '1',
1034 'source' => '',
1035 'total_amount' => '77.00',
1036 'financial_type_id' => '2', //Member dues, see data.xml
1037 'soft_credit_type_id' => 11,
1038 'soft_credit_contact_id' => '',
1039 'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
1040 'receipt_text' => 'Thank you text',
1041 'payment_processor_id' => $this->_paymentProcessorID,
1042 'credit_card_number' => '4111111111111111',
1043 'cvv2' => '123',
1044 'credit_card_exp_date' => array(
1045 'M' => '9',
1046 'Y' => '2019', // TODO: Future proof
1047 ),
1048 'credit_card_type' => 'Visa',
1049 'billing_first_name' => 'Test',
1050 'billing_middlename' => 'Last',
1051 'billing_street_address-5' => '10 Test St',
1052 'billing_city-5' => 'Test',
1053 'billing_state_province_id-5' => '1003',
1054 'billing_postal_code-5' => '90210',
1055 'billing_country_id-5' => '1228',
1056 'send_receipt' => 1,
1057 );
1058 return $params;
1059 }
1060
1061 }