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 * Test APIv3 civicrm_contribute_* functions
31 * @package CiviCRM_APIv3
32 * @subpackage API_Contribution
35 class CRM_Contribute_Form_AdditionalPaymentTest
extends CiviUnitTestCase
{
42 protected $_individualId;
45 * Parameters to create contribution.
56 protected $_contributionId;
59 * Parameters to create payment processor.
63 protected $_processorParams = [];
66 * Payment instrument mapping.
70 protected $paymentInstruments = [];
73 * Dummy payment processor.
75 * @var CRM_Core_Payment_Dummy
77 protected $paymentProcessor;
80 * Payment processor ID.
84 protected $paymentProcessorID;
89 * @throws \CRM_Core_Exception
90 * @throws \CiviCRM_API3_Exception
92 public function setUp() {
94 $this->createLoggedInUser();
96 $this->_individualId
= $this->individualCreate();
98 'total_amount' => 100,
100 'contact_id' => $this->_individualId
,
101 'financial_type_id' => 1,
103 $this->_processorParams
= [
106 'payment_processor_type_id' => 10,
107 'financial_account_id' => 12,
110 'url_site' => 'http://dummy.com',
111 'url_recur' => 'http://dummy.com',
115 $instruments = $this->callAPISuccess('contribution', 'getoptions', ['field' => 'payment_instrument_id']);
116 $this->paymentInstruments
= $instruments['values'];
118 $this->paymentProcessor
= $this->dummyProcessorCreate();
119 $processor = $this->paymentProcessor
->getPaymentProcessor();
120 $this->paymentProcessorID
= $processor['id'];
124 * Clean up after each test.
126 * @throws \CRM_Core_Exception
128 public function tearDown() {
129 $this->quickCleanUpFinancialEntities();
130 $this->quickCleanup(['civicrm_mailing_spool']);
135 * Test the submit function that completes the partially paid Contribution using Credit Card.
137 * @throws \CRM_Core_Exception
138 * @throws \CiviCRM_API3_Exception
140 public function testAddPaymentUsingCreditCardForPartiallyPaidContribution() {
141 $mut = new CiviMailUtils($this, TRUE);
142 $this->createPartiallyPaidOrder();
144 // pay additional amount by using Credit Card
145 $this->submitPayment(70, 'live', TRUE);
146 $this->checkResults([30, 70], 2);
147 $mut->assertSubjects(['Payment Receipt - Mr. Anthony Anderson II']);
149 'From: site@something.com',
152 'Total Amount: $ 100.00',
153 'This Payment Amount: $ 70.00',
154 'Balance Owed: $ 0.00 ',
155 'Billing Name and Address',
156 'Vancouver, AE 1321312',
163 $mut->clearMessages();
164 $this->validateAllPayments();
168 * Test the submit function that completes the partially paid Contribution.
170 * @throws \CRM_Core_Exception
171 * @throws \CiviCRM_API3_Exception
173 public function testAddPaymentForPartiallyPaidContribution() {
174 $this->createPartiallyPaidOrder();
176 // pay additional amount
177 $this->submitPayment(70);
178 $this->checkResults([30, 70], 2);
179 $this->validateAllPayments();
183 * Test the submit function that completes the partially paid Contribution with multiple payments.
185 * @throws \CRM_Core_Exception
186 * @throws \CiviCRM_API3_Exception
188 public function testMultiplePaymentForPartiallyPaidContribution() {
189 $this->createPartiallyPaidOrder();
191 // pay additional amount
192 $this->submitPayment(50);
193 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $this->_contributionId
]);
194 $this->assertEquals('Partially paid', $contribution['contribution_status']);
196 // pay additional amount
197 $this->submitPayment(20);
198 $this->checkResults([30, 50, 20], 3);
199 $activities = $this->callAPISuccess('Activity', 'get', [
200 'source_record_id' => $this->_contributionId
,
201 'activity_type_id' => 'Payment',
202 'options' => ['sort' => 'id'],
204 'return' => ['target_contact_id', 'assignee_contact_id', 'subject'],
206 $this->assertCount(3, $activities);
207 $this->assertEquals('$ 50.00 - Offline Payment for Contribution', $activities[1]['subject']);
208 $this->assertEquals('$ 20.00 - Offline Payment for Contribution', $activities[2]['subject']);
209 $this->assertEquals(CRM_Core_Session
::singleton()->getLoggedInContactID(), $activities[0]['source_contact_id']);
210 $this->assertEquals([$this->_individualId
], $activities[0]['target_contact_id']);
211 $this->assertEquals([], $activities[0]['assignee_contact_id']);
212 $this->validateAllPayments();
216 * Test the submit function that completes the partially paid Contribution with multiple payments.
218 * @throws \CRM_Core_Exception
219 * @throws \CiviCRM_API3_Exception
221 public function testMultiplePaymentForPartiallyPaidContributionWithOneCreditCardPayment() {
222 $mut = new CiviMailUtils($this, TRUE);
223 $this->createPartiallyPaidOrder();
224 // In general when there is tpl leakage we try to fix. At the moment, however,
225 // the tpl leakage on credit card related things is kind of 'by-design' - or
226 // at least we haven't found a way to replace the way in with Payment.send_confirmation
227 // picks them up from the form process so we will just clear templates here to stop leakage
228 // from previous tests causing a fail.
229 // The reason this is hard to fix is that we save a billing address per contribution not
230 // per payment so it's a problem with the data model
231 CRM_Core_Smarty
::singleton()->clearTemplateVars();
233 // pay additional amount
234 $this->submitPayment(50, NULL, TRUE);
235 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $this->_contributionId
]);
236 $this->assertEquals('Partially paid', $contribution['contribution_status']);
238 // pay additional amount by using credit card
239 $this->submitPayment(20, 'live');
240 $this->checkResults([30, 50, 20], 3);
241 $mut->assertSubjects(['Payment Receipt - Mr. Anthony Anderson II']);
244 'Below you will find a receipt for this payment.',
245 'Total Amount: $ 100.00',
246 'This Payment Amount: $ 50.00',
247 'Balance Owed: $ 20.00 ',
249 'Check Number: check-12345',
252 'Billing Name and Address',
256 $mut->clearMessages();
257 $this->validateAllPayments();
261 * Test the submit function that completes the pending pay later Contribution using Credit Card.
263 * @throws \CRM_Core_Exception
264 * @throws \CiviCRM_API3_Exception
266 public function testAddPaymentUsingCreditCardForPendingPayLaterContribution() {
267 $mut = new CiviMailUtils($this, TRUE);
268 $this->createPendingOrder();
270 // pay additional amount by using Credit Card
271 $this->submitPayment(100, 'live', TRUE);
272 $this->checkResults([100], 1);
275 'Below you will find a receipt for this payment.',
276 'Total Amount: $ 100.00',
277 'This Payment Amount: $ 100.00',
278 'Balance Owed: $ 0.00 ',
279 'Paid By: Credit Card',
281 'Billing Name and Address',
282 'Vancouver, AE 1321312',
287 $mut->clearMessages();
288 $this->validateAllPayments();
292 * Test the submit function that completes the pending pay later Contribution.
294 * @throws \CRM_Core_Exception
295 * @throws \CiviCRM_API3_Exception
297 public function testAddPaymentForPendingPayLaterContribution() {
298 $this->createPendingOrder();
300 // pay additional amount
301 $this->submitPayment(70);
302 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $this->_contributionId
]);
303 $this->assertEquals('Partially paid', $contribution['contribution_status']);
304 $this->assertEquals('2019-04-01 00:00:00', $contribution['receive_date']);
305 $payment = $this->callAPISuccessGetSingle('Payment', ['contribution_id' => $contribution['id']]);
306 $this->assertEquals('2017-04-11 13:05:11', $payment['trxn_date']);
308 // pay additional amount
309 $this->submitPayment(30);
310 $this->checkResults([30, 70], 2);
311 $this->validateAllPayments();
315 * Test the Membership status after completing the pending pay later Contribution.
317 * @throws \CRM_Core_Exception
318 * @throws \CiviCRM_API3_Exception
320 public function testMembershipStatusAfterCompletingPayLaterContribution() {
321 $this->createPendingOrder();
322 $membership = $this->createPendingMembershipAndRecordContribution($this->_contributionId
);
323 // pay additional amount
324 $this->submitPayment(100);
325 $this->callAPISuccessGetSingle('Contribution', ['id' => $this->_contributionId
]);
326 $contributionMembership = $this->callAPISuccessGetSingle('Membership', ['id' => $membership['id']]);
327 $membershipStatus = $this->callAPISuccessGetSingle('MembershipStatus', ['id' => $contributionMembership['status_id']]);
328 $this->assertEquals('New', $membershipStatus['name']);
329 $this->validateAllPayments();
333 * @param $contributionId
337 * @throws \CRM_Core_Exception
339 private function createPendingMembershipAndRecordContribution($contributionId) {
340 $this->_individualId
= $this->individualCreate();
341 $membershipTypeAnnualFixed = $this->callAPISuccess('membership_type', 'create', [
343 'name' => 'AnnualFixed',
344 'member_of_contact_id' => 1,
345 'duration_unit' => 'year',
346 'duration_interval' => 1,
347 'period_type' => 'fixed',
348 'fixed_period_start_day' => '101',
349 'fixed_period_rollover_day' => '1231',
350 'relationship_type_id' => 20,
351 'financial_type_id' => 2,
353 $membershipStatuses = CRM_Member_PseudoConstant
::membershipStatus();
354 $pendingStatusId = array_search('Pending', $membershipStatuses, TRUE);
355 $membership = $this->callAPISuccess('Membership', 'create', [
356 'contact_id' => $this->_individualId
,
357 'membership_type_id' => $membershipTypeAnnualFixed['id'],
359 // Updating Membership status to Pending
360 $membership = $this->callAPISuccess('Membership', 'create', [
361 'id' => $membership['id'],
362 'status_id' => $pendingStatusId,
364 $this->callAPISuccess('MembershipPayment', 'create', [
365 'membership_id' => $membership['id'],
366 'contribution_id' => $contributionId,
372 * Test the submit function that completes the pending pay later Contribution with multiple payments.
374 * @throws \CRM_Core_Exception
375 * @throws \CiviCRM_API3_Exception
377 public function testMultiplePaymentForPendingPayLaterContribution() {
378 $this->createPendingOrder();
380 // pay additional amount
381 $this->submitPayment(40);
382 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $this->_contributionId
]);
383 $this->assertEquals('Partially paid', $contribution['contribution_status']);
385 $this->submitPayment(20);
386 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $this->_contributionId
]);
387 $this->assertEquals('Partially paid', $contribution['contribution_status']);
389 $this->submitPayment(30);
390 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $this->_contributionId
]);
391 $this->assertEquals('Partially paid', $contribution['contribution_status']);
393 $this->submitPayment(10);
394 $this->checkResults([40, 20, 30, 10], 4);
395 $this->validateAllPayments();
399 * Test the submit function that completes the pending pay later Contribution with multiple payments.
401 * @throws \CRM_Core_Exception
402 * @throws \CiviCRM_API3_Exception
404 public function testMultiplePaymentForPendingPayLaterContributionWithOneCreditCardPayment() {
405 $this->createPendingOrder();
407 // pay additional amount
408 $this->submitPayment(50);
409 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $this->_contributionId
]);
410 $this->assertEquals('Partially paid', $contribution['contribution_status']);
412 $this->submitPayment(20, 'live');
413 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $this->_contributionId
]);
414 $this->assertEquals('Partially paid', $contribution['contribution_status']);
416 $this->submitPayment(20);
417 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $this->_contributionId
]);
418 $this->assertEquals('Partially paid', $contribution['contribution_status']);
420 $this->submitPayment(10, 'live');
421 $this->checkResults([50, 20, 20, 10], 4);
422 $this->validateAllPayments();
426 * Function to submit payments for contribution.
428 * @param float $amount
430 * @param string $mode
432 * @param bool $isEmailReceipt
434 * @throws \CiviCRM_API3_Exception
436 public function submitPayment($amount, $mode = NULL, $isEmailReceipt = FALSE) {
437 $form = new CRM_Contribute_Form_AdditionalPayment();
440 'contribution_id' => $this->_contributionId
,
441 'contact_id' => $this->_individualId
,
442 'total_amount' => $amount,
444 'financial_type_id' => 1,
445 'trxn_date' => '2017-04-11 13:05:11',
446 'payment_processor_id' => 0,
447 'is_email_receipt' => $isEmailReceipt,
448 'from_email_address' => 'site@something.com',
452 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments
, TRUE),
453 'payment_processor_id' => $this->paymentProcessorID
,
454 'credit_card_exp_date' => ['M' => 5, 'Y' => 2025],
455 'credit_card_number' => '411111111111111',
457 'credit_card_type' => 'Visa',
458 'billing_city-5' => 'Vancouver',
459 'billing_state_province_id-5' => 1059,
460 'billing_postal_code-5' => 1321312,
461 'billing_country_id-5' => 1228,
466 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
, TRUE),
467 'check_number' => 'check-12345',
470 $form->cid
= $this->_individualId
;
471 $form->testSubmit($submitParams, $mode);
475 * Function to check result.
477 * @param array $amounts
478 * Array of payment amount for contribution
480 * Number payment for contribution
482 * @throws \CRM_Core_Exception
484 public function checkResults($amounts, $count) {
485 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $this->_contributionId
]);
486 $this->assertNotEmpty($contribution);
487 $this->assertEquals('Completed', $contribution['contribution_status']);
489 $this->callAPISuccessGetCount('EntityFinancialTrxn', [
490 'entity_table' => 'civicrm_contribution',
491 'entity_id' => $this->_contributionId
,
492 'financial_trxn_id.is_payment' => 1,
493 'financial_trxn_id.total_amount' => ['IN' => $amounts],
498 * Create a pending order.
500 * @throws \CRM_Core_Exception
502 protected function createPendingOrder() {
503 $orderParams = array_merge($this->_params
, [
504 'contribution_status_id' => 'Pending',
506 'receive_date' => '2019-04-01',
508 $contribution = $this->callAPISuccess('Order', 'create', $orderParams);
509 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $contribution['id']]);
510 $this->assertEquals('Pending', CRM_Core_PseudoConstant
::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $contribution['contribution_status_id']));
511 $this->_contributionId
= $contribution['id'];
515 * Create a partially paid order.
517 * @throws \CRM_Core_Exception
519 protected function createPartiallyPaidOrder() {
520 $orderParams = array_merge($this->_params
, [
521 'total_amount' => 100.00,
522 'contribution_status_id' => 'Pending',
523 'api.Payment.create' => ['total_amount' => 30],
525 $contribution = $this->callAPISuccess('Order', 'create', $orderParams);
526 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $contribution['id']]);
527 $this->assertEquals('Partially paid', CRM_Core_PseudoConstant
::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $contribution['contribution_status_id']));
528 $this->_contributionId
= $contribution['id'];