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 api_v3_OrderTest
extends CiviUnitTestCase
{
37 protected $_individualId;
38 protected $_financialTypeId = 1;
44 * @throws \CRM_Core_Exception
46 public function setUp() {
49 $this->_apiversion
= 3;
50 $this->_individualId
= $this->individualCreate();
54 * Clean up after each test.
56 * @throws \CRM_Core_Exception
58 public function tearDown() {
59 $this->quickCleanUpFinancialEntities();
60 $this->quickCleanup(['civicrm_uf_match']);
66 public function testGetOrder() {
67 $contribution = $this->addOrder(FALSE, 100);
70 'contribution_id' => $contribution['id'],
73 $order = $this->callAPIAndDocument('Order', 'get', $params, __FUNCTION__
, __FILE__
);
75 $this->assertEquals(1, $order['count']);
77 $contribution['id'] => [
78 'total_amount' => 100,
79 'contribution_id' => $contribution['id'],
80 'contribution_status' => 'Completed',
85 'entity_table' => 'civicrm_contribution',
86 'entity_id' => $contribution['id'],
87 'contribution_id' => $contribution['id'],
90 'financial_type_id' => 1,
92 $this->checkPaymentResult($order, $expectedResult, $lineItems);
93 $this->callAPISuccess('Contribution', 'Delete', [
94 'id' => $contribution['id'],
99 * Test Get Order api for participant contribution.
101 * @throws \CRM_Core_Exception
103 public function testGetOrderParticipant() {
104 $this->addOrder(FALSE, 100);
105 $contribution = $this->createPartiallyPaidParticipantOrder();
108 'contribution_id' => $contribution['id'],
111 $order = $this->callAPISuccess('Order', 'get', $params);
113 $this->assertCount(2, $order['values'][$contribution['id']]['line_items']);
114 $this->callAPISuccess('Contribution', 'Delete', [
115 'id' => $contribution['id'],
120 * Function to assert db values.
122 public function checkPaymentResult($results, $expectedResult, $lineItems = NULL) {
123 foreach ($expectedResult[$results['id']] as $key => $value) {
124 $this->assertEquals($results['values'][$results['id']][$key], $value);
128 foreach ($lineItems as $key => $items) {
129 foreach ($items as $k => $item) {
130 $this->assertEquals($results['values'][$results['id']]['line_items'][$key][$k], $item);
139 * @param bool $isPriceSet
140 * @param float $amount
141 * @param array $extraParams
145 public function addOrder($isPriceSet, $amount = 300.00, $extraParams = []) {
147 'contact_id' => $this->_individualId
,
148 'receive_date' => '2010-01-20',
149 'total_amount' => $amount,
150 'financial_type_id' => $this->_financialTypeId
,
151 'contribution_status_id' => 1,
155 $priceFields = $this->createPriceSet();
156 foreach ($priceFields['values'] as $key => $priceField) {
157 $lineItems[1][$key] = [
158 'price_field_id' => $priceField['price_field_id'],
159 'price_field_value_id' => $priceField['id'],
160 'label' => $priceField['label'],
161 'field_title' => $priceField['label'],
163 'unit_price' => $priceField['amount'],
164 'line_total' => $priceField['amount'],
165 'financial_type_id' => $priceField['financial_type_id'],
168 $p['line_item'] = $lineItems;
170 $p = array_merge($extraParams, $p);
171 return $this->callAPISuccess('Contribution', 'create', $p);
175 * Test create order api
177 public function testAddOrder() {
178 $order = $this->addOrder(FALSE, 100);
180 'contribution_id' => $order['id'],
182 $order = $this->callAPISuccess('order', 'get', $params);
185 'total_amount' => 100,
186 'contribution_id' => $order['id'],
187 'contribution_status' => 'Completed',
192 'entity_table' => 'civicrm_contribution',
193 'entity_id' => $order['id'],
194 'contribution_id' => $order['id'],
197 'financial_type_id' => 1,
199 $this->checkPaymentResult($order, $expectedResult, $lineItems);
200 $this->callAPISuccess('Contribution', 'Delete', [
201 'id' => $order['id'],
206 * Test create order api for membership
208 * @throws \CRM_Core_Exception
210 public function testAddOrderForMembership() {
211 $membershipType = $this->membershipTypeCreate();
212 $membershipType1 = $this->membershipTypeCreate();
213 $membershipType = $membershipTypes = [$membershipType, $membershipType1];
215 'contact_id' => $this->_individualId
,
216 'receive_date' => '2010-01-20',
217 'financial_type_id' => 'Event Fee',
218 'contribution_status_id' => 'Pending',
220 $priceFields = $this->createPriceSet();
221 foreach ($priceFields['values'] as $key => $priceField) {
223 'price_field_id' => $priceField['price_field_id'],
224 'price_field_value_id' => $priceField['id'],
225 'label' => $priceField['label'],
226 'field_title' => $priceField['label'],
228 'unit_price' => $priceField['amount'],
229 'line_total' => $priceField['amount'],
230 'financial_type_id' => $priceField['financial_type_id'],
231 'entity_table' => 'civicrm_membership',
232 'membership_type_id' => array_pop($membershipType),
235 $p['line_items'][] = [
236 'line_item' => [array_pop($lineItems)],
238 'contact_id' => $this->_individualId
,
239 'membership_type_id' => array_pop($membershipTypes),
240 'join_date' => '2006-01-21',
241 'start_date' => '2006-01-21',
242 'end_date' => '2006-12-21',
243 'source' => 'Payment',
247 $order = $this->callAPIAndDocument('order', 'create', $p, __FUNCTION__
, __FILE__
);
249 'contribution_id' => $order['id'],
251 $order = $this->callAPISuccess('order', 'get', $params);
254 'total_amount' => 200,
255 'contribution_id' => $order['id'],
256 'contribution_status' => 'Pending Label**',
260 $this->checkPaymentResult($order, $expectedResult);
261 $this->callAPISuccessGetCount('MembershipPayment', $params, 1);
262 $this->callAPISuccess('Contribution', 'Delete', [
263 'id' => $order['id'],
265 $p['line_items'][] = [
266 'line_item' => [array_pop($lineItems)],
268 'contact_id' => $this->_individualId
,
269 'membership_type_id' => array_pop($membershipTypes),
270 'join_date' => '2006-01-21',
271 'start_date' => '2006-01-21',
272 'end_date' => '2006-12-21',
273 'source' => 'Payment',
275 'status_id' => 'Pending',
278 $p['total_amount'] = 300;
279 $order = $this->callAPISuccess('order', 'create', $p);
282 'total_amount' => 300,
283 'contribution_status' => 'Pending Label**',
287 $paymentMembership = [
288 'contribution_id' => $order['id'],
290 $order = $this->callAPISuccess('order', 'get', $paymentMembership);
291 $this->checkPaymentResult($order, $expectedResult);
292 $this->callAPISuccessGetCount('MembershipPayment', $paymentMembership, 2);
293 $this->callAPISuccess('Contribution', 'Delete', [
294 'id' => $order['id'],
299 * Test create order api for participant
301 * @throws \CRM_Core_Exception
303 public function testAddOrderForParticipant() {
304 $event = $this->eventCreate();
305 $this->_eventId
= $event['id'];
307 'contact_id' => $this->_individualId
,
308 'receive_date' => '2010-01-20',
309 'financial_type_id' => $this->_financialTypeId
,
310 'contribution_status_id' => 'Pending',
312 $priceFields = $this->createPriceSet();
313 foreach ($priceFields['values'] as $key => $priceField) {
315 'price_field_id' => $priceField['price_field_id'],
316 'price_field_value_id' => $priceField['id'],
317 'label' => $priceField['label'],
318 'field_title' => $priceField['label'],
320 'unit_price' => $priceField['amount'],
321 'line_total' => $priceField['amount'],
322 'financial_type_id' => $priceField['financial_type_id'],
323 'entity_table' => 'civicrm_participant',
326 $p['line_items'][] = [
327 'line_item' => $lineItems,
329 'contact_id' => $this->_individualId
,
330 'event_id' => $this->_eventId
,
332 'register_date' => '2007-07-21 00:00:00',
333 'source' => 'Online Event Registration: API Testing',
337 $order = $this->callAPIAndDocument('order', 'create', $p, __FUNCTION__
, __FILE__
, 'Create order for participant', 'CreateOrderParticipant');
338 $params = ['contribution_id' => $order['id']];
339 $order = $this->callAPISuccess('order', 'get', $params);
342 'total_amount' => 300,
343 'contribution_id' => $order['id'],
344 'contribution_status' => 'Pending Label**',
348 $this->checkPaymentResult($order, $expectedResult);
349 $paymentParticipant = $this->callAPISuccessGetSingle('ParticipantPayment', ['contribution_id' => $order['id']]);
350 $participant = $this->callAPISuccessGetSingle('Participant', ['participant_id' => $paymentParticipant['participant_id']]);
351 $this->assertEquals('Pending (incomplete transaction)', $participant['participant_status']);
352 $this->callAPISuccess('Contribution', 'Delete', [
353 'id' => $order['id'],
356 $p['line_items'][] = [
357 'line_item' => $lineItems,
359 'contact_id' => $this->individualCreate(),
360 'event_id' => $this->_eventId
,
362 'register_date' => '2007-07-21 00:00:00',
363 'source' => 'Online Event Registration: API Testing',
367 $order = $this->callAPISuccess('order', 'create', $p);
370 'total_amount' => 600,
371 'contribution_status' => 'Pending Label**',
375 $paymentParticipant = [
376 'contribution_id' => $order['id'],
378 $order = $this->callAPISuccess('order', 'get', $paymentParticipant);
379 $this->checkPaymentResult($order, $expectedResult);
380 $this->callAPISuccessGetCount('ParticipantPayment', $paymentParticipant, 2);
381 $this->callAPISuccess('Contribution', 'Delete', [
382 'id' => $order['id'],
387 * Test create order api with line items
389 public function testAddOrderWithLineItems() {
390 $order = $this->addOrder(TRUE);
392 'contribution_id' => $order['id'],
394 $order = $this->callAPISuccess('order', 'get', $params);
397 'total_amount' => 300,
398 'contribution_id' => $order['id'],
399 'contribution_status' => 'Completed',
404 'entity_table' => 'civicrm_contribution',
405 'entity_id' => $order['id'],
406 'contribution_id' => $order['id'],
411 'entity_table' => 'civicrm_contribution',
412 'entity_id' => $order['id'],
413 'contribution_id' => $order['id'],
417 $this->checkPaymentResult($order, $expectedResult, $items);
419 'entity_table' => 'civicrm_contribution',
420 'entity_id' => $order['id'],
422 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params);
423 $this->assertEquals($eft['values'][$eft['id']]['amount'], 300);
425 'entity_table' => 'civicrm_financial_item',
426 'financial_trxn_id' => $eft['values'][$eft['id']]['financial_trxn_id'],
428 $eft = $this->callAPISuccess('EntityFinancialTrxn', 'get', $params);
429 $amounts = [200, 100];
430 foreach ($eft['values'] as $value) {
431 $this->assertEquals($value['amount'], array_pop($amounts));
433 $this->callAPISuccess('Contribution', 'Delete', [
434 'id' => $order['id'],
439 * Test delete order api
441 public function testDeleteOrder() {
442 $order = $this->addOrder(FALSE, 100);
444 'contribution_id' => $order['id'],
447 $this->callAPISuccess('order', 'delete', $params);
448 $this->fail("Missed expected exception");
450 catch (Exception
$expected) {
451 $this->callAPISuccess('Contribution', 'create', [
452 'contribution_id' => $order['id'],
455 $this->callAPIAndDocument('order', 'delete', $params, __FUNCTION__
, __FILE__
);
456 $order = $this->callAPISuccess('order', 'get', $params);
457 $this->assertEquals(0, $order['count']);
462 * Test cancel order api
464 public function testCancelOrder() {
465 $contribution = $this->addOrder(FALSE, 100);
467 'contribution_id' => $contribution['id'],
469 $this->callAPIAndDocument('order', 'cancel', $params, __FUNCTION__
, __FILE__
);
470 $order = $this->callAPISuccess('Order', 'get', $params);
472 $contribution['id'] => [
473 'total_amount' => 100,
474 'contribution_id' => $contribution['id'],
475 'contribution_status' => 'Cancelled',
479 $this->checkPaymentResult($order, $expectedResult);
480 $this->callAPISuccess('Contribution', 'Delete', [
481 'id' => $contribution['id'],
486 * Test cancel order api
488 public function testCancelWithParticipant() {
489 $event = $this->eventCreate();
490 $this->_eventId
= $event['id'];
492 'id' => $this->_eventId
,
493 'financial_type_id' => 4,
496 $this->callAPISuccess('event', 'create', $eventParams);
497 $participantParams = [
498 'financial_type_id' => 4,
499 'event_id' => $this->_eventId
,
502 'fee_currency' => 'USD',
503 'contact_id' => $this->_individualId
,
505 $participant = $this->callAPISuccess('Participant', 'create', $participantParams);
507 'contribution_mode' => 'participant',
508 'participant_id' => $participant['id'],
510 $contribution = $this->addOrder(TRUE, 100, $extraParams);
511 $paymentParticipant = [
512 'participant_id' => $participant['id'],
513 'contribution_id' => $contribution['id'],
515 $this->callAPISuccess('ParticipantPayment', 'create', $paymentParticipant);
517 'contribution_id' => $contribution['id'],
519 $this->callAPISuccess('order', 'cancel', $params);
520 $order = $this->callAPISuccess('Order', 'get', $params);
522 $contribution['id'] => [
523 'total_amount' => 100,
524 'contribution_id' => $contribution['id'],
525 'contribution_status' => 'Cancelled',
529 $this->checkPaymentResult($order, $expectedResult);
530 $participantPayment = $this->callAPISuccess('ParticipantPayment', 'getsingle', $params);
531 $participant = $this->callAPISuccess('participant', 'get', ['id' => $participantPayment['participant_id']]);
532 $this->assertEquals($participant['values'][$participant['id']]['participant_status'], 'Cancelled');
533 $this->callAPISuccess('Contribution', 'Delete', [
534 'id' => $contribution['id'],
539 * Test an exception is thrown if line items do not add up to total_amount, no tax.
541 public function testCreateOrderIfTotalAmountDoesNotMatchLineItemsAmountsIfNoTaxSupplied() {
543 'contact_id' => $this->_individualId
,
544 'receive_date' => '2018-01-01',
545 'total_amount' => 50,
546 'financial_type_id' => $this->_financialTypeId
,
547 'contribution_status_id' => 'Pending',
552 'price_field_id' => 1,
553 'price_field_value_id' => 1,
555 'field_title' => 'Test 1',
559 'financial_type_id' => 1,
560 'entity_table' => 'civicrm_contribution',
567 $this->callAPIFailure('Order', 'create', $params, 'Line item total doesn\'t match with total amount');
571 * Test an exception is thrown if line items do not add up to total_amount, with tax.
573 public function testCreateOrderIfTotalAmountDoesNotMatchLineItemsAmountsIfTaxSupplied() {
575 'contact_id' => $this->_individualId
,
576 'receive_date' => '2018-01-01',
577 'total_amount' => 50,
578 'financial_type_id' => $this->_financialTypeId
,
579 'contribution_status_id' => 'Pending',
585 'price_field_id' => 1,
586 'price_field_value_id' => 1,
588 'field_title' => 'Test 1',
592 'financial_type_id' => 1,
593 'entity_table' => 'civicrm_contribution',
601 $this->callAPIFailure('Order', 'create', $params, 'Line item total doesn\'t match with total amount.');
604 public function testCreateOrderIfTotalAmountDoesMatchLineItemsAmountsAndTaxSupplied() {
606 'contact_id' => $this->_individualId
,
607 'receive_date' => '2018-01-01',
608 'total_amount' => 50,
609 'financial_type_id' => $this->_financialTypeId
,
610 'contribution_status_id' => 'Pending',
616 'price_field_id' => 1,
617 'price_field_value_id' => 1,
619 'field_title' => 'Test 1',
623 'financial_type_id' => 1,
624 'entity_table' => 'civicrm_contribution',
632 $order = $this->callAPISuccess('Order', 'create', $params);
633 $this->assertEquals(1, $order['count']);
637 * Test that a contribution can be added in pending mode with a chained payment.
639 * We have just deprecated creating an order with a status other than pending. It makes
640 * sense to support adding a payment straight away by chaining.
642 * @throws \CRM_Core_Exception
644 public function testCreateWithChainedPayment() {
645 $contributionID = $this->callAPISuccess('Order', 'create', ['contact_id' => $this->_individualId
, 'total_amount' => 5, 'financial_type_id' => 2, 'contribution_status_id' => 'Pending', 'api.Payment.create' => ['total_amount' => 5]])['id'];
646 $this->assertEquals('Completed', $this->callAPISuccessGetValue('Contribution', ['id' => $contributionID, 'return' => 'contribution_status']));