3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
29 * File for the MembershipTest class
33 * @author Walt Haas <walt@dharmatech.org> (801) 534-1262
37 * Test CRM_Member_Form_Membership functions.
42 class CRM_Member_Form_MembershipTest
extends CiviUnitTestCase
{
44 protected $_individualId;
45 protected $_contribution;
46 protected $_financialTypeId = 1;
47 protected $_apiversion;
48 protected $_entity = 'Membership';
51 protected $_paymentProcessorID;
54 * Membership type ID for annual fixed membership.
58 protected $membershipTypeAnnualFixedID;
61 * Parameters to create payment processor.
65 protected $_processorParams = [];
68 * ID of created membership.
72 protected $_membershipID;
75 * Payment instrument mapping.
79 protected $paymentInstruments = [];
87 * Test setup for every test.
89 * Connect to the database, truncate the tables that will be used
90 * and redirect stdin to a temporary file.
92 public function setUp() {
93 $this->_apiversion
= 3;
96 $this->_individualId
= $this->individualCreate();
97 $this->_paymentProcessorID
= $this->processorCreate();
99 $this->loadXMLDataSet(dirname(__FILE__
) . '/dataset/data.xml');
101 $membershipTypeAnnualFixed = $this->callAPISuccess('membership_type', 'create', [
103 'name' => "AnnualFixed",
104 'member_of_contact_id' => 23,
105 'duration_unit' => "year",
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,
114 $this->membershipTypeAnnualFixedID
= $membershipTypeAnnualFixed['id'];
116 $instruments = $this->callAPISuccess('contribution', 'getoptions', ['field' => 'payment_instrument_id']);
117 $this->paymentInstruments
= $instruments['values'];
121 * Clean up after each test.
123 public function tearDown() {
124 $this->quickCleanUpFinancialEntities();
127 'civicrm_relationship',
128 'civicrm_membership_type',
129 'civicrm_membership',
134 foreach ([17, 18, 23, 32] as $contactID) {
135 $this->callAPISuccess('contact', 'delete', ['id' => $contactID, 'skip_undelete' => TRUE]);
137 $this->callAPISuccess('relationship_type', 'delete', ['id' => 20]);
141 * Test CRM_Member_Form_Membership::formRule() with a parameter
142 * that has an empty contact_select_id value
144 public function testFormRuleEmptyContact() {
146 'contact_select_id' => 0,
147 'membership_type_id' => [1 => NULL],
150 $obj = new CRM_Member_Form_Membership();
151 $rc = $obj->formRule($params, $files, $obj);
152 $this->assertType('array', $rc);
153 $this->assertTrue(array_key_exists('membership_type_id', $rc));
155 $params['membership_type_id'] = [1 => 3];
156 $rc = $obj->formRule($params, $files, $obj);
157 $this->assertType('array', $rc);
158 $this->assertTrue(array_key_exists('join_date', $rc));
162 * Test that form rule fails if start date is before join date.
164 * Test CRM_Member_Form_Membership::formRule() with a parameter
165 * that has an start date before the join date and a rolling
168 public function testFormRuleRollingEarlyStart() {
170 $unixYesterday = $unixNow - (24 * 60 * 60);
171 $ymdYesterday = date('Y-m-d', $unixYesterday);
173 'join_date' => date('Y-m-d'),
174 'start_date' => $ymdYesterday,
176 'membership_type_id' => ['23', '15'],
179 $obj = new CRM_Member_Form_Membership();
180 $rc = call_user_func(['CRM_Member_Form_Membership', 'formRule'],
181 $params, $files, $obj
183 $this->assertType('array', $rc);
184 $this->assertTrue(array_key_exists('start_date', $rc));
188 * Test CRM_Member_Form_Membership::formRule() with a parameter
189 * that has an end date before the start date and a rolling
192 public function testFormRuleRollingEarlyEnd() {
194 $unixYesterday = $unixNow - (24 * 60 * 60);
195 $ymdYesterday = date('Y-m-d', $unixYesterday);
197 'join_date' => date('Y-m-d'),
198 'start_date' => date('Y-m-d'),
199 'end_date' => $ymdYesterday,
200 'membership_type_id' => ['23', '15'],
203 $obj = new CRM_Member_Form_Membership();
204 $rc = $obj->formRule($params, $files, $obj);
205 $this->assertType('array', $rc);
206 $this->assertTrue(array_key_exists('end_date', $rc));
210 * Test CRM_Member_Form_Membership::formRule() with end date but no start date and a rolling membership type.
212 public function testFormRuleRollingEndNoStart() {
214 $unixYearFromNow = $unixNow +
(365 * 24 * 60 * 60);
215 $ymdYearFromNow = date('Y-m-d', $unixYearFromNow);
217 'join_date' => date('Y-m-d'),
219 'end_date' => $ymdYearFromNow,
220 'membership_type_id' => ['23', '15'],
223 $obj = new CRM_Member_Form_Membership();
224 $rc = $obj->formRule($params, $files, $obj);
225 $this->assertType('array', $rc);
226 $this->assertTrue(array_key_exists('start_date', $rc));
230 * Test CRM_Member_Form_Membership::formRule() with a parameter
231 * that has an end date and a lifetime membership type
233 public function testFormRuleRollingLifetimeEnd() {
235 $unixYearFromNow = $unixNow +
(365 * 24 * 60 * 60);
237 'join_date' => date('Y-m-d'),
238 'start_date' => date('Y-m-d'),
239 'end_date' => date('Y-m-d',
242 'membership_type_id' => ['23', '25'],
245 $obj = new CRM_Member_Form_Membership();
246 $rc = $obj->formRule($params, $files, $obj);
247 $this->assertType('array', $rc);
248 $this->assertTrue(array_key_exists('status_id', $rc));
252 * Test CRM_Member_Form_Membership::formRule() with a parameter
253 * that has permanent override and no status
255 public function testFormRulePermanentOverrideWithNoStatus() {
258 'join_date' => date('Y-m-d'),
259 'membership_type_id' => ['23', '25'],
260 'is_override' => TRUE,
263 $obj = new CRM_Member_Form_Membership();
264 $rc = $obj->formRule($params, $files, $obj);
265 $this->assertType('array', $rc);
266 $this->assertTrue(array_key_exists('status_id', $rc));
269 public function testFormRuleUntilDateOverrideWithValidOverrideEndDate() {
271 'join_date' => date('Y-m-d'),
272 'membership_type_id' => ['23', '25'],
273 'is_override' => TRUE,
275 'status_override_end_date' => date('Y-m-d'),
278 $membershipForm = new CRM_Member_Form_Membership();
279 $validationResponse = $membershipForm->formRule($params, $files, $membershipForm);
280 $this->assertTrue($validationResponse);
283 public function testFormRuleUntilDateOverrideWithNoOverrideEndDate() {
285 'join_date' => date('Y-m-d'),
286 'membership_type_id' => ['23', '25'],
287 'is_override' => CRM_Member_StatusOverrideTypes
::UNTIL_DATE
,
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']);
298 * Test CRM_Member_Form_Membership::formRule() with a join date
299 * of one month from now and a rolling membership type
301 public function testFormRuleRollingJoin1MonthFromNow() {
303 $unix1MFmNow = $unixNow +
(31 * 24 * 60 * 60);
305 'join_date' => date('Y-m-d', $unix1MFmNow),
308 'membership_type_id' => ['23', '15'],
311 $obj = new CRM_Member_Form_Membership();
312 $rc = $obj->formRule($params, $files, $obj);
314 // Should have found no valid membership status.
315 $this->assertType('array', $rc);
316 $this->assertTrue(array_key_exists('_qf_default', $rc));
320 * Test CRM_Member_Form_Membership::formRule() with a join date of today and a rolling membership type.
322 public function testFormRuleRollingJoinToday() {
324 'join_date' => date('Y-m-d'),
327 'membership_type_id' => ['23', '15'],
330 $obj = new CRM_Member_Form_Membership();
331 $rc = $obj->formRule($params, $files, $obj);
333 // Should have found New membership status
334 $this->assertTrue($rc);
338 * Test CRM_Member_Form_Membership::formRule() with a join date
339 * of one month ago and a rolling membership type
341 public function testFormRuleRollingJoin1MonthAgo() {
343 $unix1MAgo = $unixNow - (31 * 24 * 60 * 60);
345 'join_date' => date('Y-m-d', $unix1MAgo),
348 'membership_type_id' => ['23', '15'],
351 $obj = new CRM_Member_Form_Membership();
352 $rc = $obj->formRule($params, $files, $obj);
354 // Should have found New membership status.
355 $this->assertTrue($rc);
359 * Test CRM_Member_Form_Membership::formRule() with a join date of six months ago and a rolling membership type.
361 public function testFormRuleRollingJoin6MonthsAgo() {
363 $unix6MAgo = $unixNow - (180 * 24 * 60 * 60);
365 'join_date' => date('Y-m-d', $unix6MAgo),
368 'membership_type_id' => ['23', '15'],
371 $obj = new CRM_Member_Form_Membership();
372 $rc = $obj->formRule($params, $files, $obj);
374 // Should have found Current membership status.
375 $this->assertTrue($rc);
379 * Test CRM_Member_Form_Membership::formRule() with a join date
380 * of one year+ ago and a rolling membership type
382 public function testFormRuleRollingJoin1YearAgo() {
384 $unix1YAgo = $unixNow - (370 * 24 * 60 * 60);
386 'join_date' => date('Y-m-d', $unix1YAgo),
389 'membership_type_id' => ['23', '15'],
392 $obj = new CRM_Member_Form_Membership();
393 $rc = $obj->formRule($params, $files, $obj);
395 // Should have found Grace membership status
396 $this->assertTrue($rc);
400 * Test CRM_Member_Form_Membership::formRule() with a join date
401 * of two years ago and a rolling membership type
403 public function testFormRuleRollingJoin2YearsAgo() {
405 $unix2YAgo = $unixNow - (2 * 365 * 24 * 60 * 60);
407 'join_date' => date('Y-m-d', $unix2YAgo),
410 'membership_type_id' => ['23', '15'],
413 $obj = new CRM_Member_Form_Membership();
414 $rc = $obj->formRule($params, $files, $obj);
416 // Should have found Expired membership status
417 $this->assertTrue($rc);
421 * Test CRM_Member_Form_Membership::formRule() with a join date
422 * of six months ago and a fixed membership type
424 public function testFormRuleFixedJoin6MonthsAgo() {
426 $unix6MAgo = $unixNow - (180 * 24 * 60 * 60);
428 'join_date' => date('Y-m-d', $unix6MAgo),
431 'membership_type_id' => ['23', '7'],
434 $obj = new CRM_Member_Form_Membership();
435 $rc = $obj->formRule($params, $files, $obj);
437 // Should have found Current membership status
438 $this->assertTrue($rc);
442 * Test the submit function of the membership form.
444 * @param string $thousandSeparator
446 * @dataProvider getThousandSeparators
448 public function testSubmit($thousandSeparator) {
449 CRM_Core_Session
::singleton()->getStatus(TRUE);
450 $this->setCurrencySeparators($thousandSeparator);
451 $form = $this->getForm();
453 $this->mut
= new CiviMailUtils($this, TRUE);
454 $form->_mode
= 'test';
455 $this->createLoggedInUser();
457 'cid' => $this->_individualId
,
458 'join_date' => date('2/d/Y', time()),
461 // This format reflects the 23 being the organisation & the 25 being the type.
462 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID
],
467 'total_amount' => $this->formatMoneyInput(1234.56),
468 //Member dues, see data.xml
469 'financial_type_id' => '2',
470 'soft_credit_type_id' => '',
471 'soft_credit_contact_id' => '',
472 'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
473 'payment_processor_id' => $this->_paymentProcessorID
,
474 'credit_card_number' => '4111111111111111',
476 'credit_card_exp_date' => [
478 // TODO: Future proof
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',
489 'send_receipt' => TRUE,
490 'receipt_text' => 'Receipt text',
492 $form->_contactID
= $this->_individualId
;
493 $form->testSubmit($params);
494 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
495 $this->callAPISuccessGetCount('ContributionRecur', ['contact_id' => $this->_individualId
], 0);
496 $contribution = $this->callAPISuccess('Contribution', 'get', [
497 'contact_id' => $this->_individualId
,
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(
506 'id' => $lastFinancialTrxnId['financialTrxnId'],
507 'return' => ['card_type_id', 'pan_truncation'],
510 $this->assertEquals(1, $financialTrxn['card_type_id']);
511 $this->assertEquals(1111, $financialTrxn['pan_truncation']);
513 $this->callAPISuccessGetCount('LineItem', [
514 'entity_id' => $membership['id'],
515 'entity_table' => 'civicrm_membership',
516 'contribution_id' => $contribution['id'],
519 $this->_checkFinancialRecords([
520 'id' => $contribution['id'],
521 'total_amount' => 1234.56,
522 'financial_account_id' => 2,
523 'payment_instrument_id' => $this->callAPISuccessGetValue('PaymentProcessor', [
524 'id' => $this->_paymentProcessorID
,
525 'return' => 'payment_instrument_id',
528 $this->mut
->checkMailLog([
529 CRM_Utils_Money
::format('1234.56'),
533 $this->assertEquals([
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',
540 ], CRM_Core_Session
::singleton()->getStatus());
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
547 public function testContributionUpdateOnMembershipTypeChange() {
548 // Step 1: Create a Membership via backoffice whose with 50.00 payment
549 $form = $this->getForm();
551 $this->mut
= new CiviMailUtils($this, TRUE);
552 $this->createLoggedInUser();
553 $priceSet = $this->callAPISuccess('PriceSet', 'Get', ["extends" => "CiviMember"]);
554 $form->set('priceSetId', $priceSet['id']);
555 CRM_Price_BAO_PriceSet
::buildPriceSet($form);
557 'cid' => $this->_individualId
,
558 'join_date' => date('Y-m-d'),
561 // This format reflects the 23 being the organisation & the 25 being the type.
562 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID
],
563 'record_contribution' => 1,
564 'total_amount' => 50,
565 'receive_date' => date('Y-m-d', time()) . ' 20:36:00',
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'),
568 //Member dues, see data.xml
569 'financial_type_id' => '2',
570 'payment_processor_id' => $this->_paymentProcessorID
,
572 $form->_contactID
= $this->_individualId
;
573 $form->testSubmit($params);
574 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
575 // check the membership status after partial payment, if its Pending
576 $this->assertEquals(array_search('New', CRM_Member_PseudoConstant
::membershipStatus()), $membership['status_id']);
577 $contribution = $this->callAPISuccessGetSingle('Contribution', [
578 'contact_id' => $this->_individualId
,
580 $this->assertEquals('Completed', $contribution['contribution_status']);
581 $this->assertEquals(50.00, $contribution['total_amount']);
582 $this->assertEquals(50.00, $contribution['net_amount']);
584 // Step 2: Change the membership type whose minimum free is less than earlier membership
585 $secondMembershipType = $this->callAPISuccess('membership_type', 'create', [
587 'name' => "Second Test Membership",
588 'member_of_contact_id' => 23,
589 'duration_unit' => "month",
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,
598 Civi
::settings()->set('update_contribution_on_membership_type_change', TRUE);
599 $form = $this->getForm();
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
;
606 'cid' => $this->_individualId
,
607 'join_date' => date('Y-m-d'),
610 // This format reflects the 23 being the organisation & the 25 being the type.
611 'membership_type_id' => [23, $secondMembershipType['id']],
612 'record_contribution' => 1,
614 'total_amount' => 25,
615 'receive_date' => date('Y-m-d', time()) . ' 20:36:00',
616 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
617 //Member dues, see data.xml
618 'financial_type_id' => '2',
619 'payment_processor_id' => $this->_paymentProcessorID
,
621 $form->_contactID
= $this->_individualId
;
622 $form->testSubmit($params);
623 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
624 // check the membership status after partial payment, if its Pending
625 $contribution = $this->callAPISuccessGetSingle('Contribution', [
626 'contact_id' => $this->_individualId
,
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']);
634 $this->assertEquals(-25, $payment['balance']);
638 * Test the submit function of the membership form for partial payment.
640 * @param string $thousandSeparator
641 * punctuation used to refer to thousands.
643 * @dataProvider getThousandSeparators
645 public function testSubmitPartialPayment($thousandSeparator) {
646 $this->setCurrencySeparators($thousandSeparator);
647 // Step 1: submit a partial payment for a membership via backoffice
648 $form = $this->getForm();
650 $this->mut
= new CiviMailUtils($this, TRUE);
651 $this->createLoggedInUser();
652 $priceSet = $this->callAPISuccess('PriceSet', 'Get', ["extends" => "CiviMember"]);
653 $form->set('priceSetId', $priceSet['id']);
654 $partiallyPaidAmount = 25;
655 CRM_Price_BAO_PriceSet
::buildPriceSet($form);
657 'cid' => $this->_individualId
,
658 'join_date' => date('Y-m-d'),
661 // This format reflects the 23 being the organisation & the 25 being the type.
662 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID
],
663 'receive_date' => date('Y-m-d', time()) . ' 20:36:00',
664 'record_contribution' => 1,
665 'total_amount' => $this->formatMoneyInput($partiallyPaidAmount),
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'),
668 //Member dues, see data.xml
669 'financial_type_id' => '2',
670 'payment_processor_id' => $this->_paymentProcessorID
,
672 $form->_contactID
= $this->_individualId
;
673 $form->testSubmit($params);
674 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
675 // check the membership status after partial payment, if its Pending
676 $this->assertEquals(array_search('Pending', CRM_Member_PseudoConstant
::membershipStatus()), $membership['status_id']);
677 $contribution = $this->callAPISuccessGetSingle('Contribution', [
678 'contact_id' => $this->_individualId
,
680 $this->assertEquals('Partially paid', $contribution['contribution_status']);
681 // $this->assertEquals(50.00, $contribution['total_amount']);
682 // $this->assertEquals(25.00, $contribution['net_amount']);
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();
688 'contribution_id' => $contribution['contribution_id'],
689 'contact_id' => $this->_individualId
,
690 'total_amount' => $this->formatMoneyInput($partiallyPaidAmount),
692 'financial_type_id' => 2,
693 'receive_date' => '2015-04-21 23:27:00',
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',
699 $form->cid
= $this->_individualId
;
700 $form->testSubmit($submitParams);
701 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
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']);
705 // check the contribution status and net amount after additional payment
706 $contribution = $this->callAPISuccessGetSingle('Contribution', [
707 'contact_id' => $this->_individualId
,
709 $this->assertEquals('Completed', $contribution['contribution_status']);
710 // $this->assertEquals(50.00, $contribution['net_amount']);
714 * Test the submit function of the membership form.
716 public function testSubmitRecur() {
717 CRM_Core_Session
::singleton()->getStatus(TRUE);
718 $pendingVal = $this->callAPISuccessGetValue('OptionValue', [
720 'option_group_id' => "contribution_status",
721 'label' => "Pending",
723 //Update label for Pending contribution status.
724 $this->callAPISuccess('OptionValue', 'create', [
726 'label' => "PendingEdited",
729 $form = $this->getForm();
731 $this->callAPISuccess('MembershipType', 'create', [
732 'id' => $this->membershipTypeAnnualFixedID
,
733 'duration_unit' => 'month',
734 'duration_interval' => 1,
735 'auto_renew' => TRUE,
738 $this->createLoggedInUser();
739 $params = $this->getBaseSubmitParams();
740 $form->_mode
= 'test';
741 $form->_contactID
= $this->_individualId
;
742 $form->testSubmit($params);
743 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
744 $this->callAPISuccessGetCount('ContributionRecur', ['contact_id' => $this->_individualId
], 1);
746 $contribution = $this->callAPISuccess('Contribution', 'get', [
747 'contact_id' => $this->_individualId
,
751 //Check if Membership Payment is recorded.
752 $this->callAPISuccessGetCount('MembershipPayment', [
753 'membership_id' => $membership['id'],
754 'contribution_id' => $contribution['id'],
758 $this->callAPISuccessGetCount('LineItem', [
759 'entity_id' => $membership['id'],
760 'entity_table' => 'civicrm_membership',
761 'contribution_id' => $contribution['id'],
763 $this->assertEquals([
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',
770 ], CRM_Core_Session
::singleton()->getStatus());
774 * CRM-20946: Test the financial entires especially the reversed amount,
775 * after related Contribution is cancelled
777 public function testFinancialEntiriesOnCancelledContribution() {
778 // Create two memberships for individual $this->_individualId, via a price set in the back end.
779 $this->createTwoMembershipsViaPriceSetInBackEnd($this->_individualId
);
781 // cancel the related contribution via API
782 $contribution = $this->callAPISuccessGetSingle('Contribution', [
783 'contact_id' => $this->_individualId
,
784 'contribution_status_id' => 2,
786 $this->callAPISuccess('Contribution', 'create', [
787 'id' => $contribution['id'],
788 'contribution_status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_DAO_Contribution', 'contribution_status_id', 'Cancelled'),
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'
798 $financialTrxnID = CRM_Core_DAO
::singleValueQuery($sql, [1 => [$contribution['id'], 'Int']]);
800 // fetch entity_financial_trxn records and compare their cancelled records
801 $result = $this->callAPISuccess('EntityFinancialTrxn', 'Get', [
802 'financial_trxn_id' => $financialTrxnID,
803 'entity_table' => 'civicrm_financial_item',
805 // compare the reversed amounts of respective memberships after cancelling contribution
806 $cancelledMembershipAmounts = [
811 foreach ($result['values'] as $record) {
812 $this->assertEquals($cancelledMembershipAmounts[$count], $record['amount']);
818 * Test the submit function of the membership form.
820 public function testSubmitPayLaterWithBilling() {
821 $form = $this->getForm(NULL);
823 $this->createLoggedInUser();
825 'cid' => $this->_individualId
,
826 'join_date' => date('Y-m-d'),
829 // This format reflects the 23 being the organisation & the 25 being the type.
830 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID
],
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,
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',
855 $form->_contactID
= $this->_individualId
;
857 $form->testSubmit($params);
858 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
859 $contribution = $this->callAPISuccessGetSingle('Contribution', [
860 'contact_id' => $this->_individualId
,
861 'contribution_status_id' => 2,
863 $this->assertEquals($contribution['trxn_id'], 777);
865 $this->callAPISuccessGetCount('LineItem', [
866 'entity_id' => $membership['id'],
867 'entity_table' => 'civicrm_membership',
868 'contribution_id' => $contribution['id'],
870 $this->callAPISuccessGetSingle('address', [
871 'contact_id' => $this->_individualId
,
872 'street_address' => '10 Test St',
873 'postal_code' => 90210,
878 * Test if membership is updated to New after contribution
879 * is updated from Partially paid to Completed.
881 public function testSubmitUpdateMembershipFromPartiallyPaid() {
882 $memStatus = CRM_Member_BAO_Membership
::buildOptions('status_id', 'validate');
884 //Perform a pay later membership contribution.
885 $this->testSubmitPayLaterWithBilling();
886 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
887 $this->assertEquals($membership['status_id'], array_search('Pending', $memStatus));
888 $contribution = $this->callAPISuccessGetSingle('MembershipPayment', [
889 'membership_id' => $membership['id'],
892 //Update contribution to Partially paid.
893 $prevContribution = $this->callAPISuccess('Contribution', 'create', [
894 'id' => $contribution['contribution_id'],
895 'contribution_status_id' => 'Partially paid',
897 $prevContribution = $prevContribution['values'][1];
899 //Complete the contribution from offline form.
900 $form = new CRM_Contribute_Form_Contribution();
902 'id' => $contribution['contribution_id'],
903 'contribution_status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
906 $fields = ['total_amount', 'net_amount', 'financial_type_id', 'receive_date', 'contact_id', 'payment_instrument_id'];
907 foreach ($fields as $val) {
908 $submitParams[$val] = $prevContribution[$val];
910 $form->testSubmit($submitParams, CRM_Core_Action
::UPDATE
);
912 //Check if Membership is updated to New.
913 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
914 $this->assertEquals($membership['status_id'], array_search('New', $memStatus));
918 * Test the submit function of the membership form.
920 public function testSubmitRecurCompleteInstant() {
921 $form = $this->getForm();
922 $mut = new CiviMailUtils($this, TRUE);
923 $processor = Civi\Payment\System
::singleton()->getById($this->_paymentProcessorID
);
924 $processor->setDoDirectPaymentResult([
925 'payment_status_id' => 1,
926 'trxn_id' => 'kettles boil water',
929 $processorDetail = $processor->getPaymentProcessor();
930 $this->callAPISuccess('MembershipType', 'create', [
931 'id' => $this->membershipTypeAnnualFixedID
,
932 'duration_unit' => 'month',
933 'duration_interval' => 1,
934 'auto_renew' => TRUE,
937 $this->createLoggedInUser();
938 $params = $this->getBaseSubmitParams();
939 $form->_mode
= 'test';
940 $form->_contactID
= $this->_individualId
;
941 $form->testSubmit($params);
942 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
943 $this->callAPISuccessGetCount('ContributionRecur', ['contact_id' => $this->_individualId
], 1);
945 $contribution = $this->callAPISuccess('Contribution', 'getsingle', [
946 'contact_id' => $this->_individualId
,
950 $this->assertEquals(.14, $contribution['fee_amount']);
951 $this->assertEquals('kettles boil water', $contribution['trxn_id']);
952 $this->assertEquals($processorDetail['payment_instrument_id'], $contribution['payment_instrument_id']);
954 $this->callAPISuccessGetCount('LineItem', [
955 'entity_id' => $membership['id'],
956 'entity_table' => 'civicrm_membership',
957 'contribution_id' => $contribution['id'],
960 '===========================================================
961 Billing Name and Address
962 ===========================================================
967 '===========================================================
968 Membership Information
969 ===========================================================
970 Membership Type: AnnualFixed
971 Membership Start Date: ',
972 '===========================================================
973 Credit Card Information
974 ===========================================================
984 * Test membership form with Failed Contribution.
986 public function testFormWithFailedContribution() {
987 $form = $this->getForm();
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']);
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
;
1004 $form->testSubmit($params);
1005 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
1006 $form->testSubmit($params);
1007 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
1008 $this->assertEquals($membership['status_id'], array_search('Pending', CRM_Member_PseudoConstant
::membershipStatus()));
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 .
1018 public function testTwoInheritedMembershipsViaPriceSetInBackend() {
1019 // Create an organization and give it a "Member of" relationship to $this->_individualId.
1020 $orgID = $this->organizationCreate();
1021 $relationship = $this->callAPISuccess('relationship', 'create', [
1022 'contact_id_a' => $this->_individualId
,
1023 'contact_id_b' => $orgID,
1024 'relationship_type_id' => 20,
1028 // Create two memberships for the organization, via a price set in the back end.
1029 $this->createTwoMembershipsViaPriceSetInBackEnd($orgID);
1031 // Check the primary memberships on the organization.
1032 $orgMembershipResult = $this->callAPISuccess('membership', 'get', [
1033 'contact_id' => $orgID,
1035 $this->assertEquals(2, $orgMembershipResult['count'], "2 primary memberships should have been created on the organization.");
1036 $primaryMembershipIds = [];
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.");
1042 // CRM-20955: check that correct inherited memberships were created for the individual,
1043 // for both of the primary memberships.
1044 $individualMembershipResult = $this->callAPISuccess('membership', 'get', [
1045 'contact_id' => $this->_individualId
,
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.");
1054 // CRM-20966: check that the correct membership contribution, line items
1055 // & membership_payment records were created for the organization.
1056 $contributionResult = $this->callAPISuccess('contribution', 'get', [
1057 'contact_id' => $orgID,
1059 'api.line_item.get' => [],
1060 'api.membership_payment.get' => [],
1062 $this->assertEquals(1, $contributionResult['count'], "One contribution should have been created for the organization's memberships.");
1064 $this->assertEquals(2, $contributionResult['values'][0]['api.line_item.get']['count'], "2 line items should have been created for the organization's memberships.");
1065 foreach ($contributionResult['values'][0]['api.line_item.get']['values'] as $lineItem) {
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.");
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.");
1071 foreach ($contributionResult['values'][0]['api.membership_payment.get']['values'] as $membershipPayment) {
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.");
1076 // CRM-20966: check that deleting relationship used for inheritance does not delete contribution.
1077 $this->callAPISuccess('relationship', 'delete', [
1078 'id' => $relationship['id'],
1081 $contributionResultAfterRelationshipDelete = $this->callAPISuccess('contribution', 'get', [
1082 'id' => $contributionResult['values'][0]['id'],
1083 'contact_id' => $orgID,
1085 $this->assertEquals(1, $contributionResultAfterRelationshipDelete['count'], "Contribution has been wrongly deleted.");
1089 * dev/core/issues/860:
1090 * Test creating two memberships via price set in the back end with a discount,
1091 * checking that the line items have correct amounts.
1093 public function testTwoMembershipsViaPriceSetInBackendWithDiscount() {
1094 // Register buildAmount hook to apply discount.
1095 $this->hookClass
->setHook('civicrm_buildAmount', [$this, 'buildAmountMembershipDiscount']);
1097 // Create two memberships for individual $this->_individualId, via a price set in the back end.
1098 $this->createTwoMembershipsViaPriceSetInBackEnd($this->_individualId
);
1099 $contribution = $this->callAPISuccessGetSingle('Contribution', [
1100 'contact_id' => $this->_individualId
,
1102 // Note: we can't check for the contribution total being discounted, because the total is set
1103 // when the contribution is created via $form->testSubmit(), but buildAmount isn't called
1104 // until testSubmit() runs. Fixing that might involve making testSubmit() more sophisticated,
1105 // or just hacking total_amount for this case.
1107 $lineItemResult = $this->callAPISuccess('LineItem', 'get', [
1108 'contribution_id' => $contribution['id'],
1110 $this->assertEquals(2, $lineItemResult['count']);
1111 $discountedItems = 0;
1112 foreach ($lineItemResult['values'] as $lineItem) {
1113 if (CRM_Utils_String
::startsWith($lineItem['label'], 'Long Haired Goat')) {
1114 $this->assertEquals(15.0, $lineItem['line_total']);
1115 $this->assertEquals('Long Haired Goat - one leg free!', $lineItem['label']);
1119 $this->assertEquals(1, $discountedItems);
1123 * Implements hook_civicrm_buildAmount() for testTwoMembershipsViaPriceSetInBackendWithDiscount().
1125 public function buildAmountMembershipDiscount($pageType, &$form, &$amount) {
1126 foreach ($amount as $id => $priceField) {
1127 if (is_array($priceField['options'])) {
1128 foreach ($priceField['options'] as $optionId => $option) {
1129 if ($option['membership_type_id'] == 15) {
1130 // Long Haired Goat membership discount.
1131 $amount[$id]['options'][$optionId]['amount'] = $option['amount'] * 0.75;
1132 $amount[$id]['options'][$optionId]['label'] = $option['label'] . ' - one leg free!';
1140 * Get a membership form object.
1142 * We need to instantiate the form to run preprocess, which means we have to trick it about the request method.
1144 * @return \CRM_Member_Form_Membership
1146 protected function getForm() {
1147 if (isset($_REQUEST['cid'])) {
1148 unset($_REQUEST['cid']);
1150 $form = new CRM_Member_Form_Membership();
1151 $_SERVER['REQUEST_METHOD'] = 'GET';
1152 $form->controller
= new CRM_Core_Controller();
1159 protected function getBaseSubmitParams() {
1161 'cid' => $this->_individualId
,
1162 'price_set_id' => 0,
1163 'join_date' => date('Y-m-d'),
1166 'campaign_id' => '',
1167 // This format reflects the 23 being the organisation & the 25 being the type.
1168 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID
],
1169 'auto_renew' => '1',
1174 'total_amount' => '77.00',
1175 //Member dues, see data.xml
1176 'financial_type_id' => '2',
1177 'soft_credit_type_id' => 11,
1178 'soft_credit_contact_id' => '',
1179 'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
1180 'receipt_text' => 'Thank you text',
1181 'payment_processor_id' => $this->_paymentProcessorID
,
1182 'credit_card_number' => '4111111111111111',
1184 'credit_card_exp_date' => [
1186 // TODO: Future proof
1189 'credit_card_type' => 'Visa',
1190 'billing_first_name' => 'Test',
1191 'billing_middlename' => 'Last',
1192 'billing_street_address-5' => '10 Test St',
1193 'billing_city-5' => 'Test',
1194 'billing_state_province_id-5' => '1003',
1195 'billing_postal_code-5' => '90210',
1196 'billing_country_id-5' => '1228',
1197 'send_receipt' => 1,
1204 * create two memberships for the same individual, via a price set in the back end.
1206 * @param int $contactId Id of contact on which the memberships will be created.
1208 protected function createTwoMembershipsViaPriceSetInBackEnd($contactId) {
1209 $form = $this->getForm(NULL);
1210 $form->preProcess();
1211 $this->createLoggedInUser();
1213 // create a price-set of price-field of type checkbox and each price-option corresponds to a membership type
1214 $priceSet = $this->callAPISuccess('price_set', 'create', [
1215 'is_quick_config' => 0,
1216 'extends' => 'CiviMember',
1217 'financial_type_id' => 1,
1218 'title' => 'my Page',
1220 $priceSetID = $priceSet['id'];
1221 // create respective checkbox price-field
1222 $priceField = $this->callAPISuccess('price_field', 'create', [
1223 'price_set_id' => $priceSetID,
1224 'label' => 'Memberships',
1225 'html_type' => 'Checkbox',
1227 $priceFieldID = $priceField['id'];
1228 // create two price options, each represent a membership type of amount 20 and 10 respectively
1229 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', [
1230 'price_set_id' => $priceSetID,
1231 'price_field_id' => $priceField['id'],
1232 'label' => 'Long Haired Goat',
1234 'financial_type_id' => 'Donation',
1235 'membership_type_id' => 15,
1236 'membership_num_terms' => 1,
1238 $pfvIDs = [$priceFieldValue['id'] => 1];
1239 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', [
1240 'price_set_id' => $priceSetID,
1241 'price_field_id' => $priceField['id'],
1242 'label' => 'Shoe-eating Goat',
1244 'financial_type_id' => 'Donation',
1245 'membership_type_id' => 35,
1246 'membership_num_terms' => 2,
1248 $pfvIDs[$priceFieldValue['id']] = 1;
1250 // register for both of these memberships via backoffice membership form submission
1252 'cid' => $contactId,
1253 'join_date' => date('Y-m-d'),
1256 // This format reflects the 23 being the organisation & the 25 being the type.
1257 "price_$priceFieldID" => $pfvIDs,
1258 "price_set_id" => $priceSetID,
1259 'membership_type_id' => [1 => 0],
1260 'auto_renew' => '0',
1261 'max_related' => '',
1264 'total_amount' => '30.00',
1265 //Member dues, see data.xml
1266 'financial_type_id' => '2',
1267 'soft_credit_type_id' => '',
1268 'soft_credit_contact_id' => '',
1269 'payment_instrument_id' => 4,
1270 'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
1271 'receipt_text_signup' => 'Thank you text',
1272 'payment_processor_id' => $this->_paymentProcessorID
,
1273 'record_contribution' => TRUE,
1275 'contribution_status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_DAO_Contribution', 'contribution_status_id', 'Pending'),
1276 'billing_first_name' => 'Test',
1277 'billing_middlename' => 'Last',
1278 'billing_street_address-5' => '10 Test St',
1279 'billing_city-5' => 'Test',
1280 'billing_state_province_id-5' => '1003',
1281 'billing_postal_code-5' => '90210',
1282 'billing_country_id-5' => '1228',
1284 $form->testSubmit($params);
1288 * Test membership status overrides when contribution is cancelled.
1290 public function testContributionFormStatusUpdate() {
1291 $form = new CRM_Contribute_Form_Contribution();
1293 //Create a membership with status = 'New'.
1294 $this->_individualId
= $this->createLoggedInUser();
1296 'contact_id' => $this->_individualId
,
1297 'membership_type_id' => $this->membershipTypeAnnualFixedID
,
1298 'status_id' => array_search('New', CRM_Member_PseudoConstant
::membershipStatus()),
1300 $cancelledStatusId = $this->callAPISuccessGetValue('OptionValue', [
1301 'return' => "value",
1302 'option_group_id' => "contribution_status",
1303 'name' => "Cancelled",
1306 'total_amount' => 50,
1307 'financial_type_id' => 2,
1308 'contact_id' => $this->_individualId
,
1309 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
1310 'contribution_status_id' => $cancelledStatusId,
1312 $membershipId = $this->contactMembershipCreate($memParams);
1315 'membership_id' => $membershipId,
1316 'total_amount' => 50,
1317 'financial_type_id' => 2,
1318 'contact_id' => $this->_individualId
,
1320 $contribution = CRM_Member_BAO_Membership
::recordMembershipContribution($contriParams);
1322 //Update Contribution to Cancelled.
1323 $form->_id
= $params['id'] = $contribution->id
;
1324 $form->_mode
= NULL;
1325 $form->_contactID
= $this->_individualId
;
1326 $form->testSubmit($params, CRM_Core_Action
::UPDATE
);
1327 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
1329 //Assert membership status overrides when the contribution cancelled.
1330 $this->assertEquals($membership['is_override'], TRUE);
1331 $this->assertEquals($membership['status_id'], $this->callAPISuccessGetValue('MembershipStatus', [
1333 'name' => "Cancelled",
1338 * CRM-21656: Test the submit function of the membership form if Sales Tax is enabled.
1339 * This test simulates what happens when one hits Edit on a Contribution that has both LineItems and Sales Tax components
1340 * Without making any Edits -> check that the LineItem data remain the same
1341 * In addition (a data-integrity check) -> check that the LineItem data add up to the data at the Contribution level
1343 public function testLineItemAmountOnSalesTax() {
1344 $this->enableTaxAndInvoicing();
1345 $this->relationForFinancialTypeWithFinancialAccount(2);
1346 $form = $this->getForm();
1347 $form->preProcess();
1348 $this->mut
= new CiviMailUtils($this, TRUE);
1349 $this->createLoggedInUser();
1350 $priceSet = $this->callAPISuccess('PriceSet', 'Get', ["extends" => "CiviMember"]);
1351 $form->set('priceSetId', $priceSet['id']);
1352 // we are simulating the creation of a Price Set in Administer -> CiviContribute -> Manage Price Sets so set is_quick_config = 0
1353 $this->callAPISuccess('PriceSet', 'Create', ["id" => $priceSet['id'], 'is_quick_config' => 0]);
1354 // clean the price options static variable to repopulate the options, in order to fetch tax information
1355 \Civi
::$statics['CRM_Price_BAO_PriceField']['priceOptions'] = NULL;
1356 CRM_Price_BAO_PriceSet
::buildPriceSet($form);
1357 // rebuild the price set form variable to include the tax information against each price options
1358 $form->_priceSet
= current(CRM_Price_BAO_PriceSet
::getSetDetail($priceSet['id']));
1360 'cid' => $this->_individualId
,
1361 'join_date' => date('Y-m-d'),
1364 // This format reflects the 23 being the organisation & the 25 being the type.
1365 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID
],
1366 'record_contribution' => 1,
1367 'total_amount' => 55,
1368 'receive_date' => date('Y-m-d', time()) . ' 20:36:00',
1369 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
1370 'contribution_status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
1371 //Member dues, see data.xml
1372 'financial_type_id' => 2,
1373 'payment_processor_id' => $this->_paymentProcessorID
,
1375 $form->_contactID
= $this->_individualId
;
1376 $form->testSubmit($params);
1378 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
1379 $lineItem = $this->callAPISuccessGetSingle('LineItem', ['entity_id' => $membership['id'], 'entity_table' => 'civicrm_membership']);
1380 $this->assertEquals(1, $lineItem['qty']);
1381 $this->assertEquals(50.00, $lineItem['unit_price']);
1382 $this->assertEquals(50.00, $lineItem['line_total']);
1383 $this->assertEquals(5.00, $lineItem['tax_amount']);
1385 // Simply save the 'Edit Contribution' form
1386 $form = new CRM_Contribute_Form_Contribution();
1387 $form->_context
= 'membership';
1388 $form->_values
= $this->callAPISuccessGetSingle('Contribution', ['id' => $lineItem['contribution_id'], 'return' => ['total_amount', 'net_amount', 'fee_amount', 'tax_amount']]);
1390 'contact_id' => $this->_individualId
,
1391 'id' => $lineItem['contribution_id'],
1392 'financial_type_id' => 2,
1393 'contribution_status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
1395 CRM_Core_Action
::UPDATE
);
1397 // ensure that the LineItem data remain the same
1398 $lineItem = $this->callAPISuccessGetSingle('LineItem', ['entity_id' => $membership['id'], 'entity_table' => 'civicrm_membership']);
1399 $this->assertEquals(1, $lineItem['qty']);
1400 $this->assertEquals(50.00, $lineItem['unit_price']);
1401 $this->assertEquals(50.00, $lineItem['line_total']);
1402 $this->assertEquals(5.00, $lineItem['tax_amount']);
1404 // ensure that the LineItem data add up to the data at the Contribution level
1405 $contribution = $this->callAPISuccessGetSingle('Contribution',
1407 'contribution_id' => 1,
1408 'return' => ['tax_amount', 'total_amount'],
1411 $this->assertEquals($contribution['total_amount'], $lineItem['line_total'] +
$lineItem['tax_amount']);
1412 $this->assertEquals($contribution['tax_amount'], $lineItem['tax_amount']);
1414 $financialItems = $this->callAPISuccess('FinancialItem', 'get', []);
1415 $financialItems_sum = 0;
1416 foreach ($financialItems['values'] as $financialItem) {
1417 $financialItems_sum +
= $financialItem['amount'];
1419 $this->assertEquals($contribution['total_amount'], $financialItems_sum);
1421 // reset the price options static variable so not leave any dummy data, that might hamper other unit tests
1422 \Civi
::$statics['CRM_Price_BAO_PriceField']['priceOptions'] = NULL;
1423 $this->disableTaxAndInvoicing();
1427 * Test that membership end_date is correct for multiple terms for pending contribution
1429 * @throws CiviCRM_API3_Exception
1430 * @throws \CRM_Core_Exception
1432 public function testCreatePendingWithMultipleTerms() {
1433 CRM_Core_Session
::singleton()->getStatus(TRUE);
1434 $form = $this->getForm();
1435 $form->preProcess();
1436 $this->mut
= new CiviMailUtils($this, TRUE);
1437 $this->createLoggedInUser();
1438 $membershipTypeAnnualRolling = $this->callAPISuccess('membership_type', 'create', [
1440 'name' => "AnnualRollingNew",
1441 'member_of_contact_id' => 23,
1442 'duration_unit' => "year",
1443 'minimum_fee' => 50,
1444 'duration_interval' => 1,
1445 'period_type' => "rolling",
1446 'relationship_type_id' => 20,
1447 'relationship_direction' => 'b_a',
1448 'financial_type_id' => 2,
1451 'cid' => $this->_individualId
,
1452 'join_date' => date('Y-m-d'),
1455 'contribution_status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'),
1456 'membership_type_id' => [23, $membershipTypeAnnualRolling['id']],
1457 'max_related' => '',
1459 'record_contribution' => 1,
1461 'total_amount' => $this->formatMoneyInput(150.00),
1462 'financial_type_id' => '2',
1463 'soft_credit_type_id' => '11',
1464 'soft_credit_contact_id' => '',
1465 'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
1466 'receipt_text' => '',
1468 $form->_contactID
= $this->_individualId
;
1469 $form->testSubmit($params);
1470 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
1471 $contribution = $this->callAPISuccess('Contribution', 'get', [
1472 'contact_id' => $this->_individualId
,
1474 $endDate = (new DateTime(date('Y-m-d')))->modify("+3 years")->modify("-1 day");
1475 $endDate = $endDate->format("Y-m-d");
1477 $this->assertEquals($endDate, $membership['end_date'], 'Membership end date should be ' . $endDate);
1478 $this->assertEquals(1, count($contribution['values']), 'Pending contribution should be created.');
1479 $contribution = $contribution['values'][$contribution['id']];
1480 $additionalPaymentForm = new CRM_Contribute_Form_AdditionalPayment();
1481 $additionalPaymentForm->testSubmit([
1482 'total_amount' => 150.00,
1483 'trxn_date' => date("Y-m-d H:i:s"),
1484 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
1485 'check_number' => 'check-12345',
1487 'currency' => 'USD',
1489 'financial_type_id' => 1,
1491 'payment_processor_id' => 0,
1492 'contact_id' => $this->_individualId
,
1493 'contribution_id' => $contribution['id'],
1495 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId
]);
1496 $contribution = $this->callAPISuccess('Contribution', 'get', [
1497 'contact_id' => $this->_individualId
,
1498 'contribution_status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
1500 $this->assertEquals($endDate, $membership['end_date'], 'Membership end date should be same (' . $endDate . ') after payment');
1501 $this->assertEquals(1, count($contribution['values']), 'Completed contribution should be fetched.');