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