Merge pull request #15736 from demeritcowboy/remove-old-code
[civicrm-core.git] / tests / phpunit / api / v3 / PaymentTest.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
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 * Test APIv3 civicrm_contribute_* functions
30 *
31 * @package CiviCRM_APIv3
32 * @subpackage API_Contribution
33 * @group headless
34 */
35 class api_v3_PaymentTest extends CiviUnitTestCase {
36
37 protected $_individualId;
38
39 protected $_financialTypeId = 1;
40
41 protected $_apiversion;
42
43 public $debug = 0;
44
45 /**
46 * Setup function.
47 */
48 public function setUp() {
49 parent::setUp();
50
51 $this->_apiversion = 3;
52 $this->_individualId = $this->individualCreate();
53 CRM_Core_Config::singleton()->userPermissionClass->permissions = [];
54 }
55
56 /**
57 * Clean up after each test.
58 *
59 * @throws \Exception
60 */
61 public function tearDown() {
62 $this->quickCleanUpFinancialEntities();
63 $this->quickCleanup(['civicrm_uf_match']);
64 unset(CRM_Core_Config::singleton()->userPermissionClass->permissions);
65 parent::tearDown();
66 }
67
68 /**
69 * Test Get Payment api.
70 */
71 public function testGetPayment() {
72 $p = [
73 'contact_id' => $this->_individualId,
74 'receive_date' => '2010-01-20',
75 'total_amount' => 100.00,
76 'financial_type_id' => $this->_financialTypeId,
77 'trxn_id' => 23456,
78 'contribution_status_id' => 1,
79 ];
80 $contribution = $this->callAPISuccess('contribution', 'create', $p);
81
82 $params = [
83 'contribution_id' => $contribution['id'],
84 'check_permissions' => TRUE,
85 ];
86 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM', 'administer CiviCRM'];
87 $payment = $this->callAPIFailure('payment', 'get', $params, 'API permission check failed for Payment/get call; insufficient permission: require access CiviCRM and access CiviContribute');
88
89 array_push(CRM_Core_Config::singleton()->userPermissionClass->permissions, 'access CiviContribute');
90 $payment = $this->callAPISuccess('payment', 'get', $params);
91
92 $payment = $this->callAPIAndDocument('payment', 'get', $params, __FUNCTION__, __FILE__);
93 $this->assertEquals(1, $payment['count']);
94
95 $expectedResult = [
96 $contribution['id'] => [
97 'total_amount' => 100,
98 'trxn_id' => 23456,
99 'trxn_date' => '2010-01-20 00:00:00',
100 'contribution_id' => $contribution['id'],
101 'is_payment' => 1,
102 ],
103 ];
104 $this->checkPaymentResult($payment, $expectedResult);
105 $this->callAPISuccess('Contribution', 'Delete', [
106 'id' => $contribution['id'],
107 ]);
108 }
109
110 /**
111 * Retrieve Payment using trxn_id.
112 */
113 public function testGetPaymentWithTrxnID() {
114 $this->_individualId2 = $this->individualCreate();
115 $params1 = [
116 'contact_id' => $this->_individualId,
117 'trxn_id' => 111111,
118 'total_amount' => 10,
119 ];
120 $contributionID1 = $this->contributionCreate($params1);
121
122 $params2 = [
123 'contact_id' => $this->_individualId2,
124 'trxn_id' => 222222,
125 'total_amount' => 20,
126 ];
127 $contributionID2 = $this->contributionCreate($params2);
128
129 $paymentParams = ['trxn_id' => 111111];
130 $payment = $this->callAPISuccess('payment', 'get', $paymentParams);
131 $expectedResult = [
132 $payment['id'] => [
133 'total_amount' => 10,
134 'trxn_id' => 111111,
135 'status_id' => 1,
136 'is_payment' => 1,
137 'contribution_id' => $contributionID1,
138 ],
139 ];
140 $this->checkPaymentResult($payment, $expectedResult);
141
142 $paymentParams = ['trxn_id' => 222222];
143 $payment = $this->callAPISuccess('payment', 'get', $paymentParams);
144 $expectedResult = [
145 $payment['id'] => [
146 'total_amount' => 20,
147 'trxn_id' => 222222,
148 'status_id' => 1,
149 'is_payment' => 1,
150 'contribution_id' => $contributionID2,
151 ],
152 ];
153 $this->checkPaymentResult($payment, $expectedResult);
154 }
155
156 /**
157 * Test email receipt for partial payment.
158 */
159 public function testPaymentEmailReceipt() {
160 $mut = new CiviMailUtils($this);
161 $contribution = $this->createPartiallyPaidParticipantOrder();
162 $event = $this->callAPISuccess('Event', 'get', []);
163 $this->addLocationToEvent($event['id']);
164 $params = [
165 'contribution_id' => $contribution['id'],
166 'total_amount' => 50,
167 'check_number' => '345',
168 'trxn_date' => '2018-08-13 17:57:56',
169 ];
170 $payment = $this->callAPISuccess('payment', 'create', $params);
171 $this->checkPaymentResult($payment, [
172 $payment['id'] => [
173 'from_financial_account_id' => 7,
174 'to_financial_account_id' => 6,
175 'total_amount' => 50,
176 'status_id' => 1,
177 'is_payment' => 1,
178 ],
179 ]);
180
181 $this->callAPISuccess('Payment', 'sendconfirmation', ['id' => $payment['id']]);
182 $mut->assertSubjects(['Payment Receipt - Annual CiviCRM meet - Mr. Anthony Anderson II']);
183 $mut->checkMailLog([
184 'From: "FIXME" <info@EXAMPLE.ORG>',
185 'Dear Anthony,',
186 'Total Fees: $ 300.00',
187 'This Payment Amount: $ 50.00',
188 //150 was paid in the 1st payment.
189 'Balance Owed: $ 100.00',
190 'Event Information and Location',
191 'Paid By: Check',
192 'Check Number: 345',
193 'Transaction Date: August 13th, 2018 5:57 PM',
194 'event place',
195 'streety street',
196 ]);
197 $mut->stop();
198 $mut->clearMessages();
199 }
200
201 /**
202 * Test email receipt for partial payment.
203 *
204 * @throws \CRM_Core_Exception
205 */
206 public function testPaymentEmailReceiptFullyPaid() {
207 $mut = new CiviMailUtils($this);
208 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviContribute', 'edit contributions', 'access CiviCRM'];
209 $contribution = $this->createPartiallyPaidParticipantOrder();
210
211 $params = [
212 'contribution_id' => $contribution['id'],
213 'total_amount' => 150,
214 ];
215 $payment = $this->callAPISuccess('payment', 'create', $params);
216
217 // Here we set the email to an invalid email & use check_permissions, domain email should be used.
218 $email = $this->callAPISuccess('Email', 'create', ['contact_id' => 1, 'email' => 'bob@example.com']);
219 $this->callAPISuccess('Payment', 'sendconfirmation', ['id' => $payment['id'], 'from' => $email['id'], 'check_permissions' => 1]);
220 $mut->assertSubjects(['Payment Receipt - Annual CiviCRM meet - Mr. Anthony Anderson II', 'Registration Confirmation - Annual CiviCRM meet - Mr. Anthony Anderson II']);
221 $mut->checkMailLog([
222 'From: "FIXME" <info@EXAMPLE.ORG>',
223 'Dear Anthony,',
224 'A payment has been received.',
225 'Total Fees: $ 300.00',
226 'This Payment Amount: $ 150.00',
227 'Balance Owed: $ 0.00',
228 'Thank you for completing this payment.',
229 ]);
230 $mut->stop();
231 $mut->clearMessages();
232 }
233
234 /**
235 * Test email receipt for partial payment.
236 *
237 * @dataProvider getThousandSeparators
238 *
239 * @param string $thousandSeparator
240 */
241 public function testRefundEmailReceipt($thousandSeparator) {
242 $this->setCurrencySeparators($thousandSeparator);
243 $decimalSeparator = ($thousandSeparator === ',' ? '.' : ',');
244 $mut = new CiviMailUtils($this);
245 $contribution = $this->createPartiallyPaidParticipantOrder();
246 $this->callAPISuccess('payment', 'create', [
247 'contribution_id' => $contribution['id'],
248 'total_amount' => 50,
249 'check_number' => '345',
250 'trxn_date' => '2018-08-13 17:57:56',
251 ]);
252
253 $payment = $this->callAPISuccess('payment', 'create', [
254 'contribution_id' => $contribution['id'],
255 'total_amount' => -30,
256 'trxn_date' => '2018-11-13 12:01:56',
257 'sequential' => TRUE,
258 ])['values'][0];
259
260 $expected = [
261 'from_financial_account_id' => 7,
262 'to_financial_account_id' => 6,
263 'total_amount' => -30,
264 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_FinancialTrxn', 'status_id', 'Refunded'),
265 'is_payment' => 1,
266 ];
267 foreach ($expected as $key => $value) {
268 $this->assertEquals($expected[$key], $payment[$key], 'mismatch on key ' . $key);
269 }
270
271 $this->callAPISuccess('Payment', 'sendconfirmation', ['id' => $payment['id']]);
272 $mut->assertSubjects(['Refund Notification - Annual CiviCRM meet - Mr. Anthony Anderson II']);
273 $mut->checkMailLog([
274 'Dear Anthony,',
275 'A refund has been issued based on changes in your registration selections.',
276 'Total Fees: $ 300' . $decimalSeparator . '00',
277 'Refund Amount: $ -30' . $decimalSeparator . '00',
278 'Event Information and Location',
279 'Paid By: Check',
280 'Transaction Date: November 13th, 2018 12:01 PM',
281 'You Paid: $ 170' . $decimalSeparator . '00',
282 ]);
283 $mut->stop();
284 $mut->clearMessages();
285 }
286
287 /**
288 * Test adding a payment to a pending multi-line order.
289 *
290 * @throws \CRM_Core_Exception
291 */
292 public function testCreatePaymentPendingOrderNoLineItems() {
293 $order = $this->createPendingParticipantOrder();
294 $this->callAPISuccess('Payment', 'create', [
295 'order_id' => $order['id'],
296 'total_amount' => 50,
297 ]);
298 }
299
300 /**
301 * Add participant with contribution
302 *
303 * @return array
304 *
305 * @throws \CRM_Core_Exception
306 */
307 protected function createPendingParticipantOrder() {
308 return $this->callAPISuccess('Order', 'create', $this->getParticipantOrderParams());
309 }
310
311 /**
312 * Test create payment api with no line item in params
313 *
314 * @throws \CRM_Core_Exception
315 */
316 public function testCreatePaymentNoLineItems() {
317 $contribution = $this->createPartiallyPaidParticipantOrder();
318
319 //Create partial payment
320 $params = [
321 'contribution_id' => $contribution['id'],
322 'total_amount' => 50,
323 ];
324 $payment = $this->callAPIAndDocument('payment', 'create', $params, __FUNCTION__, __FILE__);
325 $this->checkPaymentIsValid($payment['id'], $contribution['id']);
326
327 $params = [
328 'entity_table' => 'civicrm_financial_item',
329 'financial_trxn_id' => $payment['id'],
330 ];
331 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params);
332 $amounts = [33.33, 16.67];
333 foreach ($eft['values'] as $value) {
334 $this->assertEquals($value['amount'], array_pop($amounts));
335 }
336
337 // Now create payment to complete total amount of contribution
338 $params = [
339 'contribution_id' => $contribution['id'],
340 'total_amount' => 100,
341 ];
342 $payment = $this->callAPISuccess('payment', 'create', $params);
343 $expectedResult = [
344 $payment['id'] => [
345 'from_financial_account_id' => 7,
346 'to_financial_account_id' => 6,
347 'total_amount' => 100,
348 'status_id' => 1,
349 'is_payment' => 1,
350 ],
351 ];
352 $this->checkPaymentResult($payment, $expectedResult);
353 $params = [
354 'entity_table' => 'civicrm_financial_item',
355 'financial_trxn_id' => $payment['id'],
356 ];
357 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params);
358 $amounts = [66.67, 33.33];
359 foreach ($eft['values'] as $value) {
360 $this->assertEquals($value['amount'], array_pop($amounts));
361 }
362 // Check contribution for completed status
363 $contribution = $this->callAPISuccess('contribution', 'get', ['id' => $contribution['id']]);
364
365 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status'], 'Completed');
366 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 300.00);
367 $paymentParticipant = [
368 'contribution_id' => $contribution['id'],
369 ];
370 $participantPayment = $this->callAPISuccess('ParticipantPayment', 'getsingle', $paymentParticipant);
371 $participant = $this->callAPISuccess('participant', 'get', ['id' => $participantPayment['participant_id']]);
372 $this->assertEquals($participant['values'][$participant['id']]['participant_status'], 'Registered');
373 $this->callAPISuccess('Contribution', 'Delete', [
374 'id' => $contribution['id'],
375 ]);
376 }
377
378 /**
379 * Function to assert db values
380 *
381 * @throws \CRM_Core_Exception
382 */
383 public function checkPaymentResult($payment, $expectedResult) {
384 $refreshedPayment = $this->callAPISuccessGetSingle('Payment', ['financial_trxn_id' => $payment['id']]);
385 foreach ($expectedResult[$payment['id']] as $key => $value) {
386 $this->assertEquals($refreshedPayment[$key], $value, 'mismatch on ' . $key); $this->assertEquals($refreshedPayment[$key], $value, 'mismatch on ' . $key);
387 }
388 }
389
390 /**
391 * Test create payment api with line item in params
392 *
393 * @throws \CRM_Core_Exception
394 */
395 public function testCreatePaymentLineItems() {
396 $contribution = $this->createPartiallyPaidParticipantOrder();
397 $lineItems = $this->callAPISuccess('LineItem', 'get', ['contribution_id' => $contribution['id']])['values'];
398
399 // Create partial payment by passing line item array is params.
400 $params = [
401 'contribution_id' => $contribution['id'],
402 'total_amount' => 50,
403 ];
404 $amounts = [40, 10];
405 foreach ($lineItems as $id => $ignore) {
406 $params['line_item'][] = [$id => array_pop($amounts)];
407 }
408 $payment = $this->callAPIAndDocument('Payment', 'create', $params, __FUNCTION__, __FILE__, 'Payment with line item', 'CreatePaymentWithLineItems');
409 $this->checkPaymentIsValid($payment['id'], $contribution['id']);
410
411 $params = [
412 'entity_table' => 'civicrm_financial_item',
413 'financial_trxn_id' => $payment['id'],
414 'return' => ['entity_id.entity_id', 'amount'],
415 ];
416 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params)['values'];
417 $this->assertCount(2, $eft);
418 $amounts = [40, 10];
419 foreach ($eft as $value) {
420 $this->assertEquals($value['amount'], array_pop($amounts));
421 }
422
423 // Now create payment to complete total amount of contribution
424 $params = [
425 'contribution_id' => $contribution['id'],
426 'total_amount' => 100,
427 ];
428 $amounts = [80, 20];
429 foreach ($lineItems as $id => $ignore) {
430 $params['line_item'][] = [$id => array_pop($amounts)];
431 }
432 $payment = $this->callAPISuccess('Payment', 'create', $params);
433 $expectedResult = [
434 $payment['id'] => [
435 'from_financial_account_id' => 7,
436 'to_financial_account_id' => 6,
437 'total_amount' => 100,
438 'status_id' => 1,
439 'is_payment' => 1,
440 ],
441 ];
442 $this->checkPaymentResult($payment, $expectedResult);
443 $params = [
444 'entity_table' => 'civicrm_financial_item',
445 'financial_trxn_id' => $payment['id'],
446 ];
447 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params)['values'];
448 $this->assertCount(2, $eft);
449 $amounts = [80, 20];
450 foreach ($eft as $value) {
451 $this->assertEquals($value['amount'], array_pop($amounts));
452 }
453 // Check contribution for completed status
454 $contribution = $this->callAPISuccess('Contribution', 'get', ['id' => $contribution['id']]);
455
456 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status'], 'Completed');
457 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 300.00);
458 $paymentParticipant = [
459 'contribution_id' => $contribution['id'],
460 ];
461 $participantPayment = $this->callAPISuccess('ParticipantPayment', 'getsingle', $paymentParticipant);
462 $participant = $this->callAPISuccess('participant', 'get', ['id' => $participantPayment['participant_id']]);
463 $this->assertEquals($participant['values'][$participant['id']]['participant_status'], 'Registered');
464 $this->callAPISuccess('Contribution', 'Delete', [
465 'id' => $contribution['id'],
466 ]);
467 }
468
469 /**
470 * Test cancel payment api
471 *
472 * @throws \CRM_Core_Exception
473 */
474 public function testCancelPayment() {
475 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['administer CiviCRM', 'access CiviContribute'];
476 $contribution = $this->createPartiallyPaidParticipantOrder();
477
478 $params = [
479 'contribution_id' => $contribution['id'],
480 ];
481
482 $payment = $this->callAPISuccess('payment', 'get', $params);
483 $this->assertEquals(1, $payment['count']);
484
485 $cancelParams = [
486 'id' => $payment['id'],
487 'check_permissions' => TRUE,
488 ];
489 $payment = $this->callAPIFailure('payment', 'cancel', $cancelParams, 'API permission check failed for Payment/cancel call; insufficient permission: require access CiviCRM and access CiviContribute and edit contributions');
490
491 array_push(CRM_Core_Config::singleton()->userPermissionClass->permissions, 'access CiviCRM', 'edit contributions');
492
493 $this->callAPIAndDocument('payment', 'cancel', $cancelParams, __FUNCTION__, __FILE__);
494
495 $payment = $this->callAPISuccess('payment', 'get', $params);
496 $this->assertEquals(2, $payment['count']);
497 $amounts = [-150.00, 150.00];
498 foreach ($payment['values'] as $value) {
499 $this->assertEquals($value['total_amount'], array_pop($amounts), 'Mismatch total amount');
500 }
501
502 $this->callAPISuccess('Contribution', 'Delete', [
503 'id' => $contribution['id'],
504 ]);
505 }
506
507 /**
508 * Test delete payment api
509 */
510 public function testDeletePayment() {
511 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['administer CiviCRM', 'access CiviContribute'];
512 $contribution = $this->createPartiallyPaidParticipantOrder();
513
514 $params = [
515 'contribution_id' => $contribution['id'],
516 ];
517
518 $payment = $this->callAPISuccess('payment', 'get', $params);
519 $this->assertEquals(1, $payment['count']);
520
521 $deleteParams = [
522 'id' => $payment['id'],
523 'check_permissions' => TRUE,
524 ];
525 $payment = $this->callAPIFailure('payment', 'delete', $deleteParams, 'API permission check failed for Payment/delete call; insufficient permission: require access CiviCRM and access CiviContribute and delete in CiviContribute');
526
527 array_push(CRM_Core_Config::singleton()->userPermissionClass->permissions, 'access CiviCRM', 'delete in CiviContribute');
528 $this->callAPIAndDocument('payment', 'delete', $deleteParams, __FUNCTION__, __FILE__);
529
530 $payment = $this->callAPISuccess('payment', 'get', $params);
531 $this->assertEquals(0, $payment['count']);
532
533 $this->callAPISuccess('Contribution', 'Delete', [
534 'id' => $contribution['id'],
535 ]);
536 }
537
538 /**
539 * Test update payment api.
540 *
541 * 1) create a contribution for $300 with a partial payment of $150
542 * - this results in 2 financial transactions. The accounts receivable transaction is linked
543 * via entity_financial_trxns to the 2 line items. The $150 payment is not linked to the line items
544 * so the line items are fully allocated even though they are only half paid.
545 *
546 * 2) add a payment of $50 -
547 * This payment transaction IS linked to the line items so $350 of the $300 in line items is allocated
548 * but $200 is paid
549 *
550 * 3) update that payment to be $100
551 * This results in a negative and a positive payment ($50 & $100) - the negative payment results in
552 * financial_items but the positive payment does not.
553 *
554 * The final result is we have
555 * - 1 partly paid contribution of $300
556 * - payment financial_trxns totalling $250
557 * - 1 Accounts receivable financial_trxn totalling $300
558 * - 2 financial items totalling $300 linked to the Accounts receivable financial_trxn
559 * - 6 entries in the civicrm_entity_financial_trxn linked to line items - totalling $450.
560 * - 5 entries in the civicrm_entity_financial_trxn linked to contributions - totalling $550.
561 *
562 * @throws \CRM_Core_Exception
563 */
564 public function testUpdatePayment() {
565 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['administer CiviCRM', 'access CiviContribute', 'edit contributions'];
566 $contribution = $this->createPartiallyPaidParticipantOrder();
567
568 //Create partial payment by passing line item array is params
569 $params = [
570 'contribution_id' => $contribution['id'],
571 'total_amount' => 50,
572 ];
573
574 $payment = $this->callAPISuccess('payment', 'create', $params);
575 $expectedResult = [
576 $payment['id'] => [
577 'from_financial_account_id' => 7,
578 'to_financial_account_id' => 6,
579 'total_amount' => 50,
580 'status_id' => 1,
581 'is_payment' => 1,
582 ],
583 ];
584 $this->checkPaymentResult($payment, $expectedResult);
585
586 $params = [
587 'entity_table' => 'civicrm_financial_item',
588 'financial_trxn_id' => $payment['id'],
589 ];
590 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params);
591 $amounts = [33.33, 16.67];
592 foreach ($eft['values'] as $value) {
593 $this->assertEquals($value['amount'], array_pop($amounts));
594 }
595
596 // update the amount for payment
597 $params = [
598 'contribution_id' => $contribution['id'],
599 'total_amount' => 100,
600 'id' => $payment['id'],
601 'check_permissions' => TRUE,
602 ];
603 // @todo - move this permissions test to it's own test - it just confuses here.
604 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['administer CiviCRM', 'access CiviContribute'];
605 $this->callAPIFailure('payment', 'create', $params, 'API permission check failed for Payment/create call; insufficient permission: require access CiviCRM and access CiviContribute and edit contributions');
606
607 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['administer CiviCRM', 'access CiviContribute', 'access CiviCRM', 'edit contributions'];
608 $payment = $this->callAPIAndDocument('payment', 'create', $params, __FUNCTION__, __FILE__, 'Update Payment', 'UpdatePayment');
609
610 // Check for proportional cancelled payment against lineitems.
611 $minParams = [
612 'entity_table' => 'civicrm_financial_item',
613 'financial_trxn_id' => $payment['id'] - 1,
614 ];
615
616 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $minParams)['values'];
617 $this->assertCount(2, $eft);
618 $amounts = [-33.33, -16.67];
619
620 foreach ($eft as $value) {
621 $this->assertEquals($value['amount'], array_pop($amounts));
622 }
623
624 // Check for proportional updated payment against lineitems.
625 $params = [
626 'entity_table' => 'civicrm_financial_item',
627 'financial_trxn_id' => $payment['id'],
628 ];
629 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params)['values'];
630 $amounts = [66.67, 33.33];
631 foreach ($eft as $value) {
632 $this->assertEquals($value['amount'], array_pop($amounts));
633 }
634 $items = $this->callAPISuccess('FinancialItem', 'get', [])['values'];
635 $this->assertCount(2, $items);
636 $itemSum = 0;
637 foreach ($items as $item) {
638 $this->assertEquals('civicrm_line_item', $item['entity_table']);
639 $itemSum += $item['amount'];
640 }
641 $this->assertEquals(300, $itemSum);
642
643 $params = [
644 'contribution_id' => $contribution['id'],
645 ];
646 $payment = $this->callAPISuccess('payment', 'get', $params);
647 $amounts = [100.00, -50.00, 50.00, 150.00];
648 foreach ($payment['values'] as $value) {
649 $amount = array_pop($amounts);
650 $this->assertEquals($value['total_amount'], $amount, 'Mismatch total amount');
651
652 // Check entity financial trxn created properly
653 $params = [
654 'entity_id' => $contribution['id'],
655 'entity_table' => 'civicrm_contribution',
656 'financial_trxn_id' => $value['id'],
657 ];
658 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params);
659 $this->assertEquals($eft['values'][$eft['id']]['amount'], $amount);
660 }
661
662 $this->callAPISuccess('Contribution', 'Delete', [
663 'id' => $contribution['id'],
664 ]);
665 }
666
667 /**
668 * Test create payment api for paylater contribution
669 */
670 public function testCreatePaymentPayLater() {
671 $this->createLoggedInUser();
672 $processorID = $this->paymentProcessorCreate();
673 $contributionParams = [
674 'total_amount' => 100,
675 'currency' => 'USD',
676 'contact_id' => $this->_individualId,
677 'financial_type_id' => 1,
678 'contribution_status_id' => 2,
679 'is_pay_later' => 1,
680 ];
681 $contribution = $this->callAPISuccess('Contribution', 'create', $contributionParams);
682 //add payment for pay later transaction
683 $params = [
684 'contribution_id' => $contribution['id'],
685 'total_amount' => 100,
686 'card_type_id' => 'Visa',
687 'pan_truncation' => '1234',
688 'trxn_result_code' => 'Startling success',
689 'payment_instrument_id' => $processorID,
690 'trxn_id' => 1234,
691 ];
692 $payment = $this->callAPISuccess('Payment', 'create', $params);
693 $expectedResult = [
694 $payment['id'] => [
695 'from_financial_account_id' => 7,
696 'to_financial_account_id' => 6,
697 'total_amount' => 100,
698 'status_id' => 1,
699 'is_payment' => 1,
700 'card_type_id' => 1,
701 'pan_truncation' => '1234',
702 'trxn_result_code' => 'Startling success',
703 'trxn_id' => 1234,
704 'payment_instrument_id' => 1,
705 ],
706 ];
707 $this->checkPaymentResult($payment, $expectedResult);
708 // Check entity financial trxn created properly
709 $params = [
710 'entity_id' => $contribution['id'],
711 'entity_table' => 'civicrm_contribution',
712 'financial_trxn_id' => $payment['id'],
713 ];
714 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params);
715 $this->assertEquals($eft['values'][$eft['id']]['amount'], 100);
716 $params = [
717 'entity_table' => 'civicrm_financial_item',
718 'financial_trxn_id' => $payment['id'],
719 ];
720 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params);
721 $this->assertEquals($eft['values'][$eft['id']]['amount'], 100);
722 // Check contribution for completed status
723 $contribution = $this->callAPISuccess('contribution', 'get', ['id' => $contribution['id']]);
724 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status'], 'Completed');
725 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 100.00);
726 $this->callAPISuccess('Contribution', 'Delete', [
727 'id' => $contribution['id'],
728 ]);
729 }
730
731 /**
732 * Test create payment api for pay later contribution with partial payment.
733 *
734 * https://lab.civicrm.org/dev/financial/issues/69
735 */
736 public function testCreatePaymentIncompletePaymentPartialPayment() {
737 $contributionParams = [
738 'total_amount' => 100,
739 'currency' => 'USD',
740 'contact_id' => $this->_individualId,
741 'financial_type_id' => 1,
742 'contribution_status_id' => 2,
743 ];
744 $contribution = $this->callAPISuccess('Contribution', 'create', $contributionParams);
745 $this->callAPISuccess('Payment', 'create', [
746 'contribution_id' => $contribution['id'],
747 'total_amount' => 50,
748 'payment_instrument_id' => 'Cash',
749 ]);
750 $payments = $this->callAPISuccess('Payment', 'get', ['contribution_id' => $contribution['id']])['values'];
751 $this->assertCount(1, $payments);
752 }
753
754 /**
755 * Test create payment api for pay later contribution with partial payment.
756 *
757 * @throws \CRM_Core_Exception
758 */
759 public function testCreatePaymentPayLaterPartialPayment() {
760 $this->createLoggedInUser();
761 $contributionParams = [
762 'total_amount' => 100,
763 'currency' => 'USD',
764 'contact_id' => $this->_individualId,
765 'financial_type_id' => 1,
766 'contribution_status_id' => 2,
767 'is_pay_later' => 1,
768 ];
769 $contribution = $this->callAPISuccess('Order', 'create', $contributionParams);
770 //Create partial payment
771 $params = [
772 'contribution_id' => $contribution['id'],
773 'total_amount' => 60,
774 ];
775 $payment = $this->callAPISuccess('Payment', 'create', $params);
776 $expectedResult = [
777 $payment['id'] => [
778 'total_amount' => 60,
779 'status_id' => 1,
780 'is_payment' => 1,
781 ],
782 ];
783 $this->checkPaymentResult($payment, $expectedResult);
784 // Check entity financial trxn created properly
785 $params = [
786 'entity_id' => $contribution['id'],
787 'entity_table' => 'civicrm_contribution',
788 'financial_trxn_id' => $payment['id'],
789 ];
790 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params);
791 $this->assertEquals($eft['values'][$eft['id']]['amount'], 60);
792 $params = [
793 'entity_table' => 'civicrm_financial_item',
794 'financial_trxn_id' => $payment['id'],
795 ];
796 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params);
797 $this->assertEquals($eft['values'][$eft['id']]['amount'], 60);
798 $contribution = $this->callAPISuccess('contribution', 'get', ['id' => $contribution['id']]);
799 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status'], 'Partially paid');
800 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 100.00);
801 //Create full payment
802 $params = [
803 'contribution_id' => $contribution['id'],
804 'total_amount' => 40,
805 ];
806 // Rename the 'completed' status label first to check that we are not using the labels!
807 $this->callAPISuccess('OptionValue', 'get', ['name' => 'Completed', 'option_group_id' => 'contribution_status', 'api.OptionValue.create' => ['label' => 'Unicorn']]);
808 $payment = $this->callAPISuccess('Payment', 'create', $params);
809 $expectedResult = [
810 $payment['id'] => [
811 'from_financial_account_id' => 7,
812 'to_financial_account_id' => 6,
813 'total_amount' => 40,
814 'status_id' => 1,
815 'is_payment' => 1,
816 ],
817 ];
818 $this->checkPaymentResult($payment, $expectedResult);
819 // Check entity financial trxn created properly
820 $params = [
821 'entity_id' => $contribution['id'],
822 'entity_table' => 'civicrm_contribution',
823 'financial_trxn_id' => $payment['id'],
824 ];
825 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params);
826 $this->assertEquals($eft['values'][$eft['id']]['amount'], 40);
827 $params = [
828 'entity_table' => 'civicrm_financial_item',
829 'financial_trxn_id' => $payment['id'],
830 ];
831 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params);
832 $this->assertEquals($eft['values'][$eft['id']]['amount'], 40);
833 // Check contribution for completed status
834 $contribution = $this->callAPISuccess('contribution', 'get', ['id' => $contribution['id']]);
835 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status'], 'Unicorn');
836 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 100.00);
837 $this->callAPISuccess('Contribution', 'Delete', [
838 'id' => $contribution['id'],
839 ]);
840 $this->callAPISuccess('OptionValue', 'get', ['name' => 'Completed', 'option_group_id' => 'contribution_status', 'api.OptionValue.create' => ['label' => 'Completed']]);
841 $this->callAPISuccessGetCount('Activity', ['target_contact_id' => $this->_individualId, 'activity_type_id' => 'Payment'], 2);
842 }
843
844 /**
845 * Test that Payment.create uses the to_account of the payment processor.
846 *
847 * @throws \CiviCRM_API3_Exception
848 * @throws \CRM_Core_Exception
849 */
850 public function testPaymentWithProcessorWithOddFinancialAccount() {
851 $processor = $this->dummyProcessorCreate(['financial_account_id' => 'Deposit Bank Account', 'payment_instrument_id' => 'Cash']);
852 $processor2 = $this->dummyProcessorCreate(['financial_account_id' => 'Payment Processor Account', 'name' => 'p2', 'payment_instrument_id' => 'EFT']);
853 $contributionParams = [
854 'total_amount' => 100,
855 'currency' => 'USD',
856 'contact_id' => $this->_individualId,
857 'financial_type_id' => 1,
858 'contribution_status_id' => 'Pending',
859 ];
860 $order = $this->callAPISuccess('Order', 'create', $contributionParams);
861 $this->callAPISuccess('Payment', 'create', ['payment_processor_id' => $processor->getID(), 'total_amount' => 6, 'contribution_id' => $order['id']]);
862 $this->callAPISuccess('Payment', 'create', ['payment_processor_id' => $processor2->getID(), 'total_amount' => 15, 'contribution_id' => $order['id']]);
863 $payments = $this->callAPISuccess('Payment', 'get', ['sequential' => 1, 'contribution_id' => $order['id']])['values'];
864 $this->assertEquals('Deposit Bank Account', CRM_Core_PseudoConstant::getName('CRM_Core_BAO_FinancialTrxn', 'to_financial_account_id', $payments[0]['to_financial_account_id']));
865 $this->assertEquals('Payment Processor Account', CRM_Core_PseudoConstant::getName('CRM_Core_BAO_FinancialTrxn', 'to_financial_account_id', $payments[1]['to_financial_account_id']));
866 $this->assertEquals('Accounts Receivable', CRM_Core_PseudoConstant::getName('CRM_Core_BAO_FinancialTrxn', 'from_financial_account_id', $payments[0]['from_financial_account_id']));
867 $this->assertEquals('Accounts Receivable', CRM_Core_PseudoConstant::getName('CRM_Core_BAO_FinancialTrxn', 'from_financial_account_id', $payments[1]['from_financial_account_id']));
868 $this->assertEquals('Cash', CRM_Core_PseudoConstant::getName('CRM_Core_BAO_FinancialTrxn', 'payment_instrument_id', $payments[0]['payment_instrument_id']));
869 $this->assertEquals('EFT', CRM_Core_PseudoConstant::getName('CRM_Core_BAO_FinancialTrxn', 'payment_instrument_id', $payments[1]['payment_instrument_id']));
870 // $order = $this->callAPISuccessGetSingle('Order', ['id' => $processor->getID()]);
871 // $this->assertEquals('Cash', CRM_Core_PseudoConstant::getName('CRM_Core_BAO_FinancialTrxn', 'payment_instrument_id', $order['payment_instrument_id']));
872 }
873
874 /**
875 * Add a location to our event.
876 *
877 * @param int $eventID
878 *
879 * @throws \CRM_Core_Exception
880 */
881 protected function addLocationToEvent($eventID) {
882 $addressParams = [
883 'name' => 'event place',
884 'street_address' => 'streety street',
885 'location_type_id' => 1,
886 'is_primary' => 1,
887 ];
888 // api requires contact_id - perhaps incorrectly but use add to get past that.
889 $address = CRM_Core_BAO_Address::add($addressParams);
890
891 $location = $this->callAPISuccess('LocBlock', 'create', ['address_id' => $address->id]);
892 $this->callAPISuccess('Event', 'create', [
893 'id' => $eventID,
894 'loc_block_id' => $location['id'],
895 'is_show_location' => TRUE,
896 ]);
897 }
898
899 /**
900 * Check the created payment is valid.
901 *
902 * This is probably over-testing really since we are repetitively checking a basic function...
903 *
904 * @param int $paymentID
905 * @param int $contributionID
906 * @param int $amount
907 *
908 * @throws \CRM_Core_Exception
909 */
910 protected function checkPaymentIsValid($paymentID, $contributionID, $amount = 50) {
911 $payment = $this->callAPISuccess('Payment', 'getsingle', ['financial_trxn_id' => $paymentID]);
912 $this->assertEquals(7, $payment['from_financial_account_id']);
913 $this->assertEquals(6, $payment['to_financial_account_id']);
914 $this->assertEquals(1, $payment['status_id']);
915 $this->assertEquals(1, $payment['is_payment']);
916 $this->assertEquals($amount, $payment['total_amount']);
917
918 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', [
919 'entity_id' => $contributionID,
920 'entity_table' => 'civicrm_contribution',
921 'financial_trxn_id' => $payment['id'],
922 ]);
923
924 $this->assertEquals($eft['values'][$eft['id']]['amount'], $amount);
925 }
926
927 }