3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
12 use Civi\Api4\Contribution
;
13 use Civi\Api4\PriceField
;
14 use Civi\Api4\PriceFieldValue
;
15 use Civi\Api4\PriceSet
;
18 * Test APIv3 civicrm_contribute_* functions
20 * @package CiviCRM_APIv3
21 * @subpackage API_Contribution
24 class api_v3_ContributionTest
extends CiviUnitTestCase
{
26 use CRMTraits_Profile_ProfileTrait
;
27 use CRMTraits_Custom_CustomDataTrait
;
28 use CRMTraits_Financial_OrderTrait
;
29 use CRMTraits_Financial_TaxTrait
;
31 protected $_individualId;
32 protected $_contribution;
33 protected $_financialTypeId = 1;
34 protected $entity = 'Contribution';
37 protected $_pageParams = [];
40 * Payment processor ID (dummy processor).
44 protected $paymentProcessorID;
47 * Parameters to create payment processor.
51 protected $_processorParams = [];
54 * ID of created event.
66 * Should financials be checked after the test but before tear down.
70 protected $isValidateFinancialsOnPostAssert = TRUE;
75 * @throws \CiviCRM_API3_Exception
77 public function setUp(): void
{
80 $this->_apiversion
= 3;
81 $this->_individualId
= $this->individualCreate();
83 'contact_id' => $this->_individualId
,
84 'receive_date' => '20120511',
85 'total_amount' => 100.00,
86 'financial_type_id' => $this->_financialTypeId
,
87 'non_deductible_amount' => 10.00,
89 'net_amount' => 95.00,
91 'contribution_status_id' => 1,
93 $this->_processorParams
= [
96 'payment_processor_type_id' => CRM_Core_PseudoConstant
::getKey('CRM_Financial_BAO_PaymentProcessor', 'payment_processor_type_id', 'Dummy'),
97 'financial_account_id' => 12,
100 'url_site' => 'http://dummy.com',
101 'url_recur' => 'http://dummy.com',
104 $this->paymentProcessorID
= $this->processorCreate();
105 $this->_pageParams
= [
106 'title' => 'Test Contribution Page',
107 'financial_type_id' => 1,
109 'financial_account_id' => 1,
110 'payment_processor' => $this->paymentProcessorID
,
112 'is_allow_other_amount' => 1,
114 'max_amount' => 1000,
119 * Clean up after each test.
121 * @throws \CRM_Core_Exception
122 * @throws \CiviCRM_API3_Exception
124 public function tearDown(): void
{
125 $this->quickCleanUpFinancialEntities();
126 $this->quickCleanup(['civicrm_uf_match'], TRUE);
127 $financialAccounts = $this->callAPISuccess('FinancialAccount', 'get', []);
128 foreach ($financialAccounts['values'] as $financialAccount) {
129 if ($financialAccount['name'] === 'Test Tax financial account ' ||
$financialAccount['name'] === 'Test taxable financial Type') {
130 $entityFinancialTypes = $this->callAPISuccess('EntityFinancialAccount', 'get', [
131 'financial_account_id' => $financialAccount['id'],
133 foreach ($entityFinancialTypes['values'] as $entityFinancialType) {
134 $this->callAPISuccess('EntityFinancialAccount', 'delete', ['id' => $entityFinancialType['id']]);
136 $this->callAPISuccess('FinancialAccount', 'delete', ['id' => $financialAccount['id']]);
139 $this->restoreUFGroupOne();
146 * @throws \CRM_Core_Exception
148 public function testGetContribution(): void
{
149 $this->enableTaxAndInvoicing();
151 'contact_id' => $this->_individualId
,
152 'receive_date' => '2010-01-20',
153 'total_amount' => 100.00,
154 'financial_type_id' => $this->_financialTypeId
,
155 'non_deductible_amount' => 10.00,
156 'fee_amount' => 5.00,
157 'net_amount' => 95.00,
159 'invoice_id' => 78910,
161 'contribution_status_id' => 'Completed',
163 $this->_contribution
= $this->callAPISuccess('contribution', 'create', $p);
166 'contribution_id' => $this->_contribution
['id'],
169 $contributions = $this->callAPIAndDocument('contribution', 'get', $params, __FUNCTION__
, __FILE__
);
170 $financialParams['id'] = $this->_financialTypeId
;
172 CRM_Financial_BAO_FinancialType
::retrieve($financialParams, $default);
174 $this->assertEquals(1, $contributions['count']);
175 $contribution = $contributions['values'][$contributions['id']];
176 $this->assertEquals($this->_individualId
, $contribution['contact_id']);
177 $this->assertEquals(1, $contribution['financial_type_id']);
178 $this->assertEquals(100.00, $contribution['total_amount']);
179 $this->assertEquals(10.00, $contribution['non_deductible_amount']);
180 $this->assertEquals($contribution['fee_amount'], 5.00);
181 $this->assertEquals(95.00, $contribution['net_amount']);
182 $this->assertEquals(23456, $contribution['trxn_id']);
183 $this->assertEquals(78910, $contribution['invoice_id']);
184 $this->assertRegExp('/INV_\d+/', $contribution['invoice_number']);
185 $this->assertEquals('SSF', $contribution['contribution_source']);
186 $this->assertEquals('Completed', $contribution['contribution_status']);
187 // Create a second contribution - we are testing that 'id' gets the right contribution id (not the contact id).
188 $p['trxn_id'] = '3847';
189 $p['invoice_id'] = '3847';
191 $contribution2 = $this->callAPISuccess('contribution', 'create', $p);
193 // Now we have 2 - test getcount.
194 $contribution = $this->callAPISuccess('contribution', 'getcount');
195 $this->assertEquals(2, $contribution);
196 // Test id only format.
197 $contribution = $this->callAPISuccess('contribution', 'get', [
198 'id' => $this->_contribution
['id'],
199 'format.only_id' => 1,
201 $this->assertEquals($this->_contribution
['id'], $contribution, print_r($contribution, TRUE));
202 // Test id only format.
203 $contribution = $this->callAPISuccess('contribution', 'get', [
204 'id' => $contribution2['id'],
205 'format.only_id' => 1,
207 $this->assertEquals($contribution2['id'], $contribution);
209 $contribution = $this->callAPISuccess('contribution', 'get', [
210 'id' => $this->_contribution
['id'],
212 $this->assertEquals(1, $contribution['count']);
214 // Test get by contact id works.
215 $contribution = $this->callAPISuccess('contribution', 'get', ['contact_id' => $this->_individualId
]);
217 $this->assertEquals(2, $contribution['count']);
218 $this->callAPISuccess('Contribution', 'Delete', [
219 'id' => $this->_contribution
['id'],
221 $this->callAPISuccess('Contribution', 'Delete', [
222 'id' => $contribution2['id'],
227 * Test that test contributions can be retrieved.
229 * @throws \CRM_Core_Exception
231 public function testGetTestContribution(): void
{
232 $this->callAPISuccess('Contribution', 'create', array_merge($this->_params
, ['is_test' => 1]));
233 $this->callAPISuccessGetSingle('Contribution', ['is_test' => 1]);
237 * Test Creating a check contribution with original check_number field
239 * @throws \CRM_Core_Exception
241 public function testCreateCheckContribution(): void
{
242 $params = $this->_params
;
243 $params['contribution_check_number'] = 'bouncer';
244 $params['payment_instrument_id'] = 'Check';
245 $params['cancel_date'] = 'yesterday';
246 $params['receipt_date'] = 'yesterday';
247 $params['thankyou_date'] = 'yesterday';
248 $params['revenue_recognition_date'] = 'yesterday';
249 $params['amount_level'] = 'Unreasonable';
250 $params['cancel_reason'] = 'You lose sucker';
251 $params['creditnote_id'] = 'sudo rm -rf';
252 $address = $this->callAPISuccess('Address', 'create', [
253 'street_address' => 'Knockturn Alley',
254 'contact_id' => $this->_individualId
,
255 'location_type_id' => 'Home',
257 $params['address_id'] = $address['id'];
258 $contributionPage = $this->contributionPageCreate();
259 $params['contribution_page_id'] = $contributionPage['id'];
260 $params['campaign_id'] = $this->campaignCreate();
261 $contributionID = $this->contributionCreate($params);
262 $getResult = $this->callAPISuccess('Contribution', 'get', ['id' => $contributionID]);
263 $this->assertEquals('bouncer', $getResult['values'][$contributionID]['check_number']);
264 $entityFinancialTrxn = $this->callAPISuccess('EntityFinancialTrxn', 'get', ['entity_id' => $contributionID, 'entity_table' => 'civicrm_contribution']);
265 foreach ($entityFinancialTrxn['values'] as $eft) {
266 $financialTrxn = $this->callAPISuccess('FinancialTrxn', 'get', ['id' => $eft['financial_trxn_id']]);
267 $this->assertEquals('bouncer', $financialTrxn['values'][$financialTrxn['id']]['check_number']);
272 * Test the 'return' param works for all fields.
274 * @throws \CRM_Core_Exception
276 public function testGetContributionReturnFunctionality(): void
{
277 $params = $this->_params
;
278 $params['contribution_check_number'] = 'bouncer';
279 $params['payment_instrument_id'] = 'Check';
280 $params['cancel_date'] = 'yesterday';
281 $params['receipt_date'] = 'yesterday';
282 $params['thankyou_date'] = 'yesterday';
283 $params['revenue_recognition_date'] = 'yesterday';
284 $params['amount_level'] = 'Unreasonable';
285 $params['cancel_reason'] = 'You lose sucker';
286 $params['creditnote_id'] = 'sudo rm -rf';
287 $address = $this->callAPISuccess('Address', 'create', [
288 'street_address' => 'Knockturn Alley',
289 'contact_id' => $this->_individualId
,
290 'location_type_id' => 'Home',
292 $params['address_id'] = $address['id'];
293 $contributionPage = $this->contributionPageCreate();
294 $params['contribution_page_id'] = $contributionPage['id'];
295 $contributionRecur = $this->callAPISuccess('ContributionRecur', 'create', [
296 'contact_id' => $this->_individualId
,
297 'frequency_interval' => 1,
300 $params['contribution_recur_id'] = $contributionRecur['id'];
302 $params['campaign_id'] = $this->campaignCreate();
304 $contributionID = $this->contributionCreate($params);
306 // update contribution with invoice number
307 $params = array_merge($params, [
308 'id' => $contributionID,
309 'invoice_number' => CRM_Utils_Array
::value('invoice_prefix', Civi
::settings()->get('contribution_invoice_settings')) . "" . $contributionID,
311 'invoice_id' => 6789,
313 $contributionID = $this->contributionCreate($params);
315 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $contributionID]);
316 $this->assertEquals('bouncer', $contribution['check_number']);
317 $this->assertEquals('bouncer', $contribution['contribution_check_number']);
319 $fields = CRM_Contribute_BAO_Contribution
::fields();
320 // Do not check for tax_amount as this test has not enabled invoicing
321 // & hence it is not reliable.
322 unset($fields['tax_amount']);
323 // Re-add these 2 to the fields to check. They were locked in but the metadata changed so we
324 // need to specify them.
325 $fields['address_id'] = $fields['contribution_address_id'];
326 $fields['check_number'] = $fields['contribution_check_number'];
329 'contribution_id', 'contribution_contact_id', 'financial_type_id', 'contribution_page_id',
330 'payment_instrument_id', 'receive_date', 'non_deductible_amount', 'total_amount',
331 'fee_amount', 'net_amount', 'trxn_id', 'invoice_id', 'currency', 'contribution_cancel_date', 'cancel_reason',
332 'receipt_date', 'thankyou_date', 'contribution_source', 'amount_level', 'contribution_recur_id',
333 'is_test', 'is_pay_later', 'contribution_status_id', 'address_id', 'check_number', 'contribution_campaign_id',
334 'creditnote_id', 'revenue_recognition_date', 'decoy',
336 $missingFields = array_diff($fieldsLockedIn, array_keys($fields));
337 // If any of the locked in fields disappear from the $fields array we need to make sure it is still
338 // covered as the test contract now guarantees them in the return array.
339 $this->assertEquals([28 => 'decoy'], $missingFields, 'A field which was covered by the test contract has changed.');
340 foreach ($fields as $fieldName => $fieldSpec) {
341 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $contributionID, 'return' => $fieldName]);
342 $returnField = $fieldName;
343 if ($returnField === 'contribution_contact_id') {
344 $returnField = 'contact_id';
346 $this->assertTrue((!empty($contribution[$returnField]) ||
$contribution[$returnField] === "0"), $returnField);
348 $entityFinancialTrxn = $this->callAPISuccess('EntityFinancialTrxn', 'get', ['entity_id' => $contributionID, 'entity_table' => 'civicrm_contribution']);
349 foreach ($entityFinancialTrxn['values'] as $eft) {
350 $financialTrxn = $this->callAPISuccess('FinancialTrxn', 'get', ['id' => $eft['financial_trxn_id']]);
351 $this->assertEquals('bouncer', $financialTrxn['values'][$financialTrxn['id']]['check_number']);
356 * Test cancel reason works as a filter.
358 * @throws \CRM_Core_Exception
360 public function testFilterCancelReason(): void
{
361 $params = $this->_params
;
362 $params['cancel_date'] = 'yesterday';
363 $params['cancel_reason'] = 'You lose sucker';
364 $this->callAPISuccess('Contribution', 'create', $params);
365 $params = $this->_params
;
366 $params['cancel_date'] = 'yesterday';
367 $params['cancel_reason'] = 'You are a winner';
368 $this->callAPISuccess('Contribution', 'create', $params);
369 $this->callAPISuccessGetCount('Contribution', ['cancel_reason' => 'You are a winner'], 1);
373 * We need to ensure previous tested api contract behaviour still works.
375 * @throws \CRM_Core_Exception
377 public function testGetContributionLegacyBehaviour(): void
{
379 'contact_id' => $this->_individualId
,
380 'receive_date' => '2010-01-20',
381 'total_amount' => 100.00,
382 'contribution_type_id' => $this->_financialTypeId
,
383 'non_deductible_amount' => 10.00,
384 'fee_amount' => 5.00,
385 'net_amount' => 95.00,
387 'invoice_id' => 78910,
389 'contribution_status_id' => 1,
391 $this->_contribution
= $this->callAPISuccess('Contribution', 'create', $p);
394 'contribution_id' => $this->_contribution
['id'],
396 $contribution = $this->callAPISuccess('contribution', 'get', $params);
397 $financialParams['id'] = $this->_financialTypeId
;
399 CRM_Financial_BAO_FinancialType
::retrieve($financialParams, $default);
401 $this->assertEquals(1, $contribution['count']);
402 $this->assertEquals($contribution['values'][$contribution['id']]['contact_id'], $this->_individualId
);
403 $this->assertEquals($contribution['values'][$contribution['id']]['financial_type_id'], $this->_financialTypeId
);
404 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_type_id'], $this->_financialTypeId
);
405 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 100.00);
406 $this->assertEquals($contribution['values'][$contribution['id']]['non_deductible_amount'], 10.00);
407 $this->assertEquals($contribution['values'][$contribution['id']]['fee_amount'], 5.00);
408 $this->assertEquals($contribution['values'][$contribution['id']]['net_amount'], 95.00);
409 $this->assertEquals($contribution['values'][$contribution['id']]['trxn_id'], 23456);
410 $this->assertEquals($contribution['values'][$contribution['id']]['invoice_id'], 78910);
411 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_source'], 'SSF');
412 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status'], 'Completed');
414 // Create a second contribution - we are testing that 'id' gets the right contribution id (not the contact id).
415 $p['trxn_id'] = '3847';
416 $p['invoice_id'] = '3847';
418 $contribution2 = $this->callAPISuccess('contribution', 'create', $p);
420 // now we have 2 - test getcount
421 $contribution = $this->callAPISuccess('contribution', 'getcount', []);
422 $this->assertEquals(2, $contribution);
423 //test id only format
424 $contribution = $this->callAPISuccess('contribution', 'get', [
425 'id' => $this->_contribution
['id'],
426 'format.only_id' => 1,
428 $this->assertEquals($this->_contribution
['id'], $contribution, print_r($contribution, TRUE));
429 //test id only format
430 $contribution = $this->callAPISuccess('contribution', 'get', [
431 'id' => $contribution2['id'],
432 'format.only_id' => 1,
434 $this->assertEquals($contribution2['id'], $contribution);
435 $contribution = $this->callAPISuccess('contribution', 'get', [
436 'id' => $this->_contribution
['id'],
439 $this->assertEquals(1, $contribution['count']);
440 // $this->assertEquals($this->_contribution['id'], $contribution['id'] ) ;
441 //test get by contact id works
442 $contribution = $this->callAPISuccess('contribution', 'get', ['contact_id' => $this->_individualId
]);
444 $this->assertEquals(2, $contribution['count']);
445 $this->callAPISuccess('Contribution', 'Delete', [
446 'id' => $this->_contribution
['id'],
448 $this->callAPISuccess('Contribution', 'Delete', [
449 'id' => $contribution2['id'],
454 * Create an contribution_id=FALSE and financial_type_id=Donation.
456 public function testCreateEmptyContributionIDUseDonation() {
458 'contribution_id' => FALSE,
461 'check_permissions' => FALSE,
462 'financial_type_id' => 'Donation',
464 $this->callAPISuccess('contribution', 'create', $params);
468 * Check with complete array + custom field.
470 * Note that the test is written on purpose without any
471 * variables specific to participant so it can be replicated into other entities
472 * and / or moved to the automated test suite
474 * @throws \CRM_Core_Exception
476 public function testCreateWithCustom(): void
{
477 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
479 $params = $this->_params
;
480 $params['custom_' . $ids['custom_field_id']] = "custom string";
482 $result = $this->callAPIAndDocument($this->entity
, 'create', $params, __FUNCTION__
, __FILE__
);
483 $this->assertEquals($result['id'], $result['values'][$result['id']]['id']);
484 $check = $this->callAPISuccess($this->entity
, 'get', [
485 'return.custom_' . $ids['custom_field_id'] => 1,
486 'id' => $result['id'],
488 $group = $this->callAPISuccess('CustomGroup', 'getsingle', ['id' => $ids['custom_group_id']]);
489 $field = $this->callAPISuccess('CustomField', 'getsingle', ['id' => $ids['custom_field_id']]);
490 $contribution = \Civi\Api4\Contribution
::get()
494 $group['name'] . '.' . $field['name'],
496 ->addWhere('id', '=', $result['id'])
499 $this->customFieldDelete($ids['custom_field_id']);
500 $this->customGroupDelete($ids['custom_group_id']);
501 $this->assertEquals('custom string', $check['values'][$check['id']]['custom_' . $ids['custom_field_id']]);
505 * Check with complete array + custom field.
507 * Note that the test is written on purpose without any
508 * variables specific to participant so it can be replicated into other
509 * entities and / or moved to the automated test suite
511 * @throws \CRM_Core_Exception
513 public function testCreateGetFieldsWithCustom(): void
{
514 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
515 $idsContact = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, 'ContactTest.php');
516 $result = $this->callAPISuccess('Contribution', 'getfields', []);
517 $this->assertArrayHasKey('custom_' . $ids['custom_field_id'], $result['values']);
518 $this->assertArrayNotHasKey('custom_' . $idsContact['custom_field_id'], $result['values']);
519 $this->customFieldDelete($ids['custom_field_id']);
520 $this->customGroupDelete($ids['custom_group_id']);
521 $this->customFieldDelete($idsContact['custom_field_id']);
522 $this->customGroupDelete($idsContact['custom_group_id']);
526 * Test creating a contribution without skipLineItems.
528 * @throws \CRM_Core_Exception
530 public function testCreateContributionNoLineItems(): void
{
531 // Turn off this validation as this test results in invalid
532 // financial entities.
533 $this->isValidateFinancialsOnPostAssert
= FALSE;
535 'contact_id' => $this->_individualId
,
536 'receive_date' => '20120511',
537 'total_amount' => 100.00,
538 'financial_type_id' => $this->_financialTypeId
,
539 'payment_instrument_id' => 1,
540 'non_deductible_amount' => 10.00,
541 'fee_amount' => 50.00,
542 'net_amount' => 90.00,
544 'invoice_id' => 67890,
546 'contribution_status_id' => 1,
550 $contribution = $this->callAPISuccess('contribution', 'create', $params);
551 $financialItems = $this->callAPISuccess('FinancialItem', 'get', []);
552 foreach ($financialItems['values'] as $financialItem) {
553 $this->assertEquals(date('Y-m-d H:i:s', strtotime($contribution['values'][$contribution['id']]['receive_date'])), date('Y-m-d H:i:s', strtotime($financialItem['transaction_date'])));
555 $lineItems = $this->callAPISuccess('line_item', 'get', [
556 'entity_id' => $contribution['id'],
557 'entity_table' => 'civicrm_contribution',
560 $this->assertEquals(0, $lineItems['count']);
564 * Test checks that passing in line items suppresses the create mechanism.
566 * @throws \CRM_Core_Exception
568 public function testCreateContributionChainedLineItems(): void
{
570 'contact_id' => $this->_individualId
,
571 'receive_date' => '20120511',
572 'total_amount' => 100.00,
573 'financial_type_id' => $this->_financialTypeId
,
574 'payment_instrument_id' => 1,
575 'non_deductible_amount' => 10.00,
576 'fee_amount' => 50.00,
577 'net_amount' => 90.00,
579 'invoice_id' => 67890,
581 'contribution_status_id' => 'Pending',
583 'api.line_item.create' => [
585 'price_field_id' => 1,
587 'line_total' => '20',
588 'unit_price' => '10',
591 'price_field_id' => 1,
593 'line_total' => '80',
594 'unit_price' => '80',
599 $description = 'Create Contribution with Nested Line Items.';
600 $subFile = 'CreateWithNestedLineItems';
601 $contribution = $this->callAPIAndDocument('Contribution', 'create', $params, __FUNCTION__
, __FILE__
, $description, $subFile);
603 $lineItems = $this->callAPISuccess('line_item', 'get', [
604 'entity_id' => $contribution['id'],
605 'contribution_id' => $contribution['id'],
606 'entity_table' => 'civicrm_contribution',
609 $this->assertEquals(2, $lineItems['count']);
613 * @throws \CRM_Core_Exception
615 public function testCreateContributionOffline(): void
{
617 'contact_id' => $this->_individualId
,
618 'receive_date' => '20120511',
619 'total_amount' => 100.00,
620 'financial_type_id' => 1,
622 'invoice_id' => 67890,
624 'contribution_status_id' => 1,
628 $contribution = $this->callAPISuccess('Contribution', 'create', $params)['values'][0];
629 $this->assertEquals($this->_individualId
, $contribution['contact_id']);
630 $this->assertEquals(100.00, $contribution['total_amount']);
631 $this->assertEquals(1, $contribution['financial_type_id']);
632 $this->assertEquals(12345, $contribution['trxn_id']);
633 $this->assertEquals(67890, $contribution['invoice_id']);
634 $this->assertEquals('SSF', $contribution['source']);
635 $this->assertEquals(1, $contribution['contribution_status_id']);
636 $lineItems = $this->callAPISuccess('LineItem', 'get', [
637 'entity_id' => $contribution['id'],
638 'contribution_id' => $contribution['id'],
639 'entity_table' => 'civicrm_contribution',
642 $this->assertEquals(1, $lineItems['count']);
643 $this->assertEquals($contribution['id'], $lineItems['values'][0]['entity_id']);
644 $this->assertEquals($contribution['id'], $lineItems['values'][0]['contribution_id']);
645 $this->_checkFinancialRecords($contribution, 'offline');
646 $this->contributionGetnCheck($params, $contribution['id']);
650 * Test create with valid payment instrument.
652 * @throws \CRM_Core_Exception
654 public function testCreateContributionWithPaymentInstrument(): void
{
655 $params = $this->_params +
['payment_instrument' => 'EFT'];
656 $contribution = $this->callAPISuccess('contribution', 'create', $params);
657 $contribution = $this->callAPISuccess('contribution', 'get', [
659 'id' => $contribution['id'],
661 $this->assertArrayHasKey('payment_instrument', $contribution['values'][0]);
662 $this->assertEquals('EFT', $contribution['values'][0]['payment_instrument']);
664 $this->callAPISuccess('contribution', 'create', [
665 'id' => $contribution['id'],
666 'payment_instrument' => 'Credit Card',
668 $contribution = $this->callAPISuccess('contribution', 'get', [
670 'id' => $contribution['id'],
672 $this->assertArrayHasKey('payment_instrument', $contribution['values'][0]);
673 $this->assertEquals('Credit Card', $contribution['values'][0]['payment_instrument']);
677 * @throws \CRM_Core_Exception
679 public function testGetContributionByPaymentInstrument(): void
{
680 $params = $this->_params +
['payment_instrument' => 'EFT'];
681 $params2 = $this->_params +
['payment_instrument' => 'Cash'];
682 $this->callAPISuccess('contribution', 'create', $params);
683 $this->callAPISuccess('contribution', 'create', $params2);
684 $contribution = $this->callAPISuccess('contribution', 'get', [
686 'contribution_payment_instrument' => 'Cash',
688 $this->assertArrayHasKey('payment_instrument', $contribution['values'][0]);
689 $this->assertEquals('Cash', $contribution['values'][0]['payment_instrument']);
690 $this->assertEquals(1, $contribution['count']);
691 $contribution = $this->callAPISuccess('contribution', 'get', ['sequential' => 1, 'payment_instrument' => 'Cash']);
692 $this->assertArrayHasKey('payment_instrument', $contribution['values'][0]);
693 $this->assertEquals('Cash', $contribution['values'][0]['payment_instrument']);
694 $this->assertEquals(1, $contribution['count']);
695 $contribution = $this->callAPISuccess('contribution', 'get', [
697 'payment_instrument_id' => 5,
699 $this->assertArrayHasKey('payment_instrument', $contribution['values'][0]);
700 $this->assertEquals('EFT', $contribution['values'][0]['payment_instrument']);
701 $this->assertEquals(1, $contribution['count']);
702 $contribution = $this->callAPISuccess('contribution', 'get', [
704 'payment_instrument' => 'EFT',
706 $this->assertArrayHasKey('payment_instrument', $contribution['values'][0]);
707 $this->assertEquals('EFT', $contribution['values'][0]['payment_instrument']);
708 $this->assertEquals(1, $contribution['count']);
709 $contribution = $this->callAPISuccess('contribution', 'create', [
710 'id' => $contribution['id'],
711 'payment_instrument' => 'Credit Card',
713 $contribution = $this->callAPISuccess('contribution', 'get', ['sequential' => 1, 'id' => $contribution['id']]);
714 $this->assertArrayHasKey('payment_instrument', $contribution['values'][0]);
715 $this->assertEquals('Credit Card', $contribution['values'][0]['payment_instrument']);
716 $this->assertEquals(1, $contribution['count']);
720 * CRM-16227 introduces invoice_id as a parameter.
722 * @throws \CRM_Core_Exception
724 public function testGetContributionByInvoice(): void
{
725 $this->callAPISuccess('Contribution', 'create', array_merge($this->_params
, ['invoice_id' => 'curly']));
726 $this->callAPISuccess('Contribution', 'create', array_merge($this->_params
), ['invoice_id' => 'churlish']);
727 $this->callAPISuccessGetCount('Contribution', [], 2);
728 $this->callAPISuccessGetSingle('Contribution', ['invoice_id' => 'curly']);
729 // The following don't work. They are the format we are trying to introduce but although the form uses this format
730 // CRM_Contact_BAO_Query::convertFormValues puts them into the other format & the where only supports that.
731 // ideally the where clause would support this format (as it does on contact_BAO_Query) and those lines would
732 // come out of convertFormValues
733 // $this->callAPISuccessGetSingle('Contribution', array('invoice_id' => array('LIKE' => '%ish%')));
734 // $this->callAPISuccessGetSingle('Contribution', array('invoice_id' => array('NOT IN' => array('curly'))));
735 // $this->callAPISuccessGetCount('Contribution', array('invoice_id' => array('LIKE' => '%ly%')), 2);
736 // $this->callAPISuccessGetCount('Contribution', array('invoice_id' => array('IN' => array('curly', 'churlish'))),
741 * Check the credit note retrieval is case insensitive.
743 * @throws \CRM_Core_Exception
745 public function testGetCreditNoteCaseInsensitive(): void
{
746 $this->contributionCreate(['contact_id' => $this->_individualId
]);
747 $this->contributionCreate(['creditnote_id' => 'cN1234', 'contact_id' => $this->_individualId
, 'invoice_id' => 91011, 'trxn_id' => 456]);
748 $contribution = $this->callAPISuccess('Contribution', 'getsingle', ['creditnote_id' => 'CN1234']);
749 $this->assertEquals('cN1234', $contribution['creditnote_id']);
753 * Test retrieval by total_amount works.
755 * @throws \CRM_Core_Exception
757 public function testGetContributionByTotalAmount(): void
{
758 $this->callAPISuccess('Contribution', 'create', array_merge($this->_params
, ['total_amount' => '5']));
759 $this->callAPISuccess('Contribution', 'create', array_merge($this->_params
, ['total_amount' => '10']));
760 $this->callAPISuccessGetCount('Contribution', ['total_amount' => 10], 1);
761 $this->callAPISuccessGetCount('Contribution', ['total_amount' => ['>' => 6]], 1);
762 $this->callAPISuccessGetCount('Contribution', ['total_amount' => ['>' => 0]], 2);
763 $this->callAPISuccessGetCount('Contribution', ['total_amount' => ['>' => -5]], 2);
764 $this->callAPISuccessGetCount('Contribution', ['total_amount' => ['<' => 0]], 0);
765 $this->callAPISuccessGetCount('Contribution', [], 2);
769 * @dataProvider createLocalizedContributionDataProvider
771 * @param float|int|string $totalAmount
772 * @param string $decimalPoint
773 * @param string $thousandSeparator
774 * @param string $currency
775 * @param bool $expectedResult
777 * @throws \CRM_Core_Exception
779 public function testCreateLocalizedContribution($totalAmount, string $decimalPoint, string $thousandSeparator, string $currency, bool $expectedResult): void
{
780 $this->setDefaultCurrency($currency);
781 $this->setMonetaryDecimalPoint($decimalPoint);
782 $this->setMonetaryThousandSeparator($thousandSeparator);
785 'contact_id' => $this->_individualId
,
786 'receive_date' => '20120511',
787 'total_amount' => $totalAmount,
788 'financial_type_id' => $this->_financialTypeId
,
789 'contribution_status_id' => 1,
792 if ($expectedResult) {
793 $this->callAPISuccess('Contribution', 'create', $_params);
796 $this->callAPIFailure('Contribution', 'create', $_params);
803 public function createLocalizedContributionDataProvider(): array {
805 [10, '.', ',', 'USD', TRUE],
806 ['145.0E+3', '.', ',', 'USD', FALSE],
807 ['10', '.', ',', 'USD', TRUE],
808 [-10, '.', ',', 'USD', TRUE],
809 ['-10', '.', ',', 'USD', TRUE],
810 ['-10foo', '.', ',', 'USD', FALSE],
811 ['-10.0345619', '.', ',', 'USD', TRUE],
812 ['-10.010,4345619', '.', ',', 'USD', TRUE],
813 ['10.0104345619', '.', ',', 'USD', TRUE],
814 ['-0', '.', ',', 'USD', TRUE],
815 ['-.1', '.', ',', 'USD', TRUE],
816 ['.1', '.', ',', 'USD', TRUE],
817 // Test currency symbols too, default locale uses $, so if we wanted to test others we'd need to reconfigure locale
818 ['$1,234,567.89', '.', ',', 'USD', TRUE],
819 ['-$1,234,567.89', '.', ',', 'USD', TRUE],
820 ['$-1,234,567.89', '.', ',', 'USD', TRUE],
821 // This is the float format. Encapsulated in strings
822 ['1234567.89', '.', ',', 'USD', TRUE],
823 // This is the float format.
824 [1234567.89, '.', ',', 'USD', TRUE],
825 // Test EURO currency
826 ['€1,234,567.89', '.', ',', 'EUR', TRUE],
827 ['-€1,234,567.89', '.', ',', 'EUR', TRUE],
828 ['€-1,234,567.89', '.', ',', 'EUR', TRUE],
829 // This is the float format. Encapsulated in strings
830 ['1234567.89', '.', ',', 'EUR', TRUE],
831 // This is the float format.
832 [1234567.89, '.', ',', 'EUR', TRUE],
833 // Test Norwegian KR currency
834 ['kr1,234,567.89', '.', ',', 'NOK', TRUE],
835 ['kr 1,234,567.89', '.', ',', 'NOK', TRUE],
836 ['-kr1,234,567.89', '.', ',', 'NOK', TRUE],
837 ['-kr 1,234,567.89', '.', ',', 'NOK', TRUE],
838 ['kr-1,234,567.89', '.', ',', 'NOK', TRUE],
839 ['kr -1,234,567.89', '.', ',', 'NOK', TRUE],
840 // This is the float format. Encapsulated in strings
841 ['1234567.89', '.', ',', 'NOK', TRUE],
842 // This is the float format.
843 [1234567.89, '.', ',', 'NOK', TRUE],
844 // Test different localization options: , as decimal separator and dot as thousand separator
845 ['$1.234.567,89', ',', '.', 'USD', TRUE],
846 ['-$1.234.567,89', ',', '.', 'USD', TRUE],
847 ['$-1.234.567,89', ',', '.', 'USD', TRUE],
848 ['1.234.567,89', ',', '.', 'USD', TRUE],
849 // This is the float format. Encapsulated in strings
850 ['1234567.89', ',', '.', 'USD', TRUE],
851 // This is the float format.
852 [1234567.89, ',', '.', 'USD', TRUE],
853 ['$1,234,567.89', ',', '.', 'USD', FALSE],
854 ['-$1,234,567.89', ',', '.', 'USD', FALSE],
855 ['$-1,234,567.89', ',', '.', 'USD', FALSE],
856 // Now with a space as thousand separator
857 ['$1 234 567,89', ',', ' ', 'USD', TRUE],
858 ['-$1 234 567,89', ',', ' ', 'USD', TRUE],
859 ['$-1 234 567,89', ',', ' ', 'USD', TRUE],
860 ['1 234 567,89', ',', ' ', 'USD', TRUE],
861 // This is the float format. Encapsulated in strings
862 ['1234567.89', ',', ' ', 'USD', TRUE],
863 // This is the float format.
864 [1234567.89, ',', ' ', 'USD', TRUE],
869 * Create test with unique field name on source.
871 public function testCreateContributionSource() {
874 'contact_id' => $this->_individualId
,
875 'receive_date' => date('Ymd'),
876 'total_amount' => 100.00,
877 'financial_type_id' => $this->_financialTypeId
,
878 'payment_instrument_id' => 1,
879 'non_deductible_amount' => 10.00,
880 'fee_amount' => 50.00,
881 'net_amount' => 90.00,
883 'invoice_id' => 67890,
884 'contribution_source' => 'SSF',
885 'contribution_status_id' => 1,
888 $contribution = $this->callAPISuccess('contribution', 'create', $params);
889 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 100.00);
890 $this->assertEquals($contribution['values'][$contribution['id']]['source'], 'SSF');
894 * Create test with unique field name on source.
896 * @param string $thousandSeparator
897 * punctuation used to refer to thousands.
899 * @dataProvider getThousandSeparators
901 public function testCreateDefaultNow($thousandSeparator) {
902 $this->setCurrencySeparators($thousandSeparator);
903 $params = $this->_params
;
904 unset($params['receive_date'], $params['net_amount']);
906 $params['total_amount'] = $this->formatMoneyInput(5000.77);
907 $params['fee_amount'] = $this->formatMoneyInput(.77);
908 $params['skipCleanMoney'] = FALSE;
910 $contribution = $this->callAPISuccess('contribution', 'create', $params);
911 $contribution = $this->callAPISuccessGetSingle('contribution', ['id' => $contribution['id']]);
912 $this->assertEquals(date('Y-m-d'), date('Y-m-d', strtotime($contribution['receive_date'])));
913 $this->assertEquals(5000.77, $contribution['total_amount'], 'failed to handle ' . $this->formatMoneyInput(5000.77));
914 $this->assertEquals(.77, $contribution['fee_amount']);
915 $this->assertEquals(5000, $contribution['net_amount']);
919 * Create test with unique field name on source.
921 public function testCreateContributionNullOutThankyouDate() {
923 $params = $this->_params
;
924 $params['thankyou_date'] = 'yesterday';
926 $contribution = $this->callAPISuccess('contribution', 'create', $params);
927 $contribution = $this->callAPISuccessGetSingle('contribution', ['id' => $contribution['id']]);
928 $this->assertEquals(date('Y-m-d', strtotime('yesterday')), date('Y-m-d', strtotime($contribution['thankyou_date'])));
930 $params['thankyou_date'] = 'null';
931 $contribution = $this->callAPISuccess('contribution', 'create', $params);
932 $this->assertTrue(empty($contribution['thankyou_date']));
936 * Create test with unique field name on source.
938 public function testCreateContributionSourceInvalidContact() {
942 'receive_date' => date('Ymd'),
943 'total_amount' => 100.00,
944 'financial_type_id' => $this->_financialTypeId
,
945 'payment_instrument_id' => 1,
946 'non_deductible_amount' => 10.00,
947 'fee_amount' => 50.00,
948 'net_amount' => 90.00,
950 'invoice_id' => 67890,
951 'contribution_source' => 'SSF',
952 'contribution_status_id' => 1,
955 $this->callAPIFailure('contribution', 'create', $params, 'contact_id is not valid : 999');
959 * Test note created correctly.
961 * @throws \CRM_Core_Exception
963 public function testCreateContributionWithNote(): void
{
964 $description = 'Demonstrates creating contribution with Note Entity.';
965 $subFile = 'ContributionCreateWithNote';
967 'contact_id' => $this->_individualId
,
968 'receive_date' => '2012-01-01',
969 'total_amount' => 100.00,
970 'financial_type_id' => $this->_financialTypeId
,
971 'payment_instrument_id' => 1,
972 'non_deductible_amount' => 10.00,
973 'fee_amount' => 50.00,
974 'net_amount' => 90.00,
976 'invoice_id' => 67890,
978 'contribution_status_id' => 1,
979 'note' => 'my contribution note',
982 $contribution = $this->callAPIAndDocument('contribution', 'create', $params, __FUNCTION__
, __FILE__
, $description, $subFile);
983 $result = $this->callAPISuccess('note', 'get', [
984 'entity_table' => 'civicrm_contribution',
985 'entity_id' => $contribution['id'],
988 $this->assertEquals('my contribution note', $result['values'][0]['note']);
989 $this->callAPISuccess('contribution', 'delete', ['id' => $contribution['id']]);
993 * @throws \CRM_Core_Exception
995 public function testCreateContributionWithNoteUniqueNameAliases(): void
{
997 'contact_id' => $this->_individualId
,
998 'receive_date' => '2012-01-01',
999 'total_amount' => 100.00,
1000 'financial_type_id' => $this->_financialTypeId
,
1001 'payment_instrument_id' => 1,
1002 'non_deductible_amount' => 10.00,
1003 'fee_amount' => 50.00,
1004 'net_amount' => 90.00,
1006 'invoice_id' => 67890,
1008 'contribution_status_id' => 1,
1009 'contribution_note' => 'my contribution note',
1012 $contribution = $this->callAPISuccess('contribution', 'create', $params);
1013 $result = $this->callAPISuccess('note', 'get', [
1014 'entity_table' => 'civicrm_contribution',
1015 'entity_id' => $contribution['id'],
1018 $this->assertEquals('my contribution note', $result['values'][0]['note']);
1019 $this->callAPISuccess('contribution', 'delete', ['id' => $contribution['id']]);
1023 * This is the test for creating soft credits.
1025 public function testCreateContributionWithSoftCredit() {
1026 $description = "Demonstrates creating contribution with SoftCredit.";
1027 $subfile = "ContributionCreateWithSoftCredit";
1028 $contact2 = $this->callAPISuccess('Contact', 'create', [
1029 'display_name' => 'superman',
1030 'contact_type' => 'Individual',
1033 'contact_id' => $contact2['id'],
1035 'soft_credit_type_id' => 3,
1038 $params = $this->_params +
['soft_credit' => [1 => $softParams]];
1039 $contribution = $this->callAPIAndDocument('contribution', 'create', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
1040 $result = $this->callAPISuccess('contribution', 'get', ['return' => 'soft_credit', 'sequential' => 1]);
1042 $this->assertEquals($softParams['contact_id'], $result['values'][0]['soft_credit'][1]['contact_id']);
1043 $this->assertEquals($softParams['amount'], $result['values'][0]['soft_credit'][1]['amount']);
1044 $this->assertEquals($softParams['soft_credit_type_id'], $result['values'][0]['soft_credit'][1]['soft_credit_type']);
1046 $this->callAPISuccess('contribution', 'delete', ['id' => $contribution['id']]);
1047 $this->callAPISuccess('contact', 'delete', ['id' => $contact2['id']]);
1050 public function testCreateContributionWithSoftCreditDefaults() {
1051 $description = "Demonstrates creating contribution with Soft Credit defaults for amount and type.";
1052 $subfile = "ContributionCreateWithSoftCreditDefaults";
1053 $contact2 = $this->callAPISuccess('Contact', 'create', [
1054 'display_name' => 'superman',
1055 'contact_type' => 'Individual',
1057 $params = $this->_params +
[
1058 'soft_credit_to' => $contact2['id'],
1060 $contribution = $this->callAPIAndDocument('contribution', 'create', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
1061 $result = $this->callAPISuccess('contribution', 'get', ['return' => 'soft_credit', 'sequential' => 1]);
1063 $this->assertEquals($contact2['id'], $result['values'][0]['soft_credit'][1]['contact_id']);
1064 // Default soft credit amount = contribution.total_amount
1065 $this->assertEquals($this->_params
['total_amount'], $result['values'][0]['soft_credit'][1]['amount']);
1066 $this->assertEquals(CRM_Core_OptionGroup
::getDefaultValue("soft_credit_type"), $result['values'][0]['soft_credit'][1]['soft_credit_type']);
1068 $this->callAPISuccess('contribution', 'delete', ['id' => $contribution['id']]);
1069 $this->callAPISuccess('contact', 'delete', ['id' => $contact2['id']]);
1073 * Test creating contribution with Soft Credit by passing in honor_contact_id.
1075 public function testCreateContributionWithHonoreeContact() {
1076 $description = "Demonstrates creating contribution with Soft Credit by passing in honor_contact_id.";
1077 $subfile = "ContributionCreateWithHonoreeContact";
1078 $contact2 = $this->callAPISuccess('Contact', 'create', [
1079 'display_name' => 'superman',
1080 'contact_type' => 'Individual',
1082 $params = $this->_params +
[
1083 'honor_contact_id' => $contact2['id'],
1085 $contribution = $this->callAPIAndDocument('contribution', 'create', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
1086 $result = $this->callAPISuccess('contribution', 'get', ['return' => 'soft_credit', 'sequential' => 1]);
1088 $this->assertEquals($contact2['id'], $result['values'][0]['soft_credit'][1]['contact_id']);
1089 // Default soft credit amount = contribution.total_amount
1090 // Legacy mode in create api (honor_contact_id param) uses the standard "In Honor of" soft credit type
1091 $this->assertEquals($this->_params
['total_amount'], $result['values'][0]['soft_credit'][1]['amount']);
1092 $softCreditValueTypeID = $result['values'][0]['soft_credit'][1]['soft_credit_type'];
1093 $this->assertEquals('in_honor_of', CRM_Core_PseudoConstant
::getName('CRM_Contribute_BAO_ContributionSoft', 'soft_credit_type_id', $softCreditValueTypeID));
1095 $this->callAPISuccess('contribution', 'delete', ['id' => $contribution['id']]);
1096 $this->callAPISuccess('contact', 'delete', ['id' => $contact2['id']]);
1100 * Test using example code.
1102 public function testContributionCreateExample() {
1103 //make sure at least on page exists since there is a truncate in tear down
1104 $this->callAPISuccess('contribution_page', 'create', $this->_pageParams
);
1105 require_once 'api/v3/examples/Contribution/Create.ex.php';
1106 $result = contribution_create_example();
1107 $id = $result['id'];
1108 $expectedResult = contribution_create_expectedresult();
1109 $this->checkArrayEquals($expectedResult, $result);
1110 $this->contributionDelete($id);
1114 * Function tests that additional financial records are created when fee amount is recorded.
1116 public function testCreateContributionWithFee() {
1118 'contact_id' => $this->_individualId
,
1119 'receive_date' => '20120511',
1120 'total_amount' => 100.00,
1122 'financial_type_id' => 1,
1124 'invoice_id' => 67890,
1126 'contribution_status_id' => 1,
1129 $contribution = $this->callAPISuccess('contribution', 'create', $params);
1130 $this->assertEquals($contribution['values'][$contribution['id']]['contact_id'], $this->_individualId
);
1131 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 100.00);
1132 $this->assertEquals($contribution['values'][$contribution['id']]['fee_amount'], 50.00);
1133 $this->assertEquals($contribution['values'][$contribution['id']]['net_amount'], 50.00);
1134 $this->assertEquals($contribution['values'][$contribution['id']]['financial_type_id'], 1);
1135 $this->assertEquals($contribution['values'][$contribution['id']]['trxn_id'], 12345);
1136 $this->assertEquals($contribution['values'][$contribution['id']]['invoice_id'], 67890);
1137 $this->assertEquals($contribution['values'][$contribution['id']]['source'], 'SSF');
1138 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status_id'], 1);
1140 $lineItems = $this->callAPISuccess('line_item', 'get', [
1142 'entity_id' => $contribution['id'],
1143 'entity_table' => 'civicrm_contribution',
1146 $this->assertEquals(1, $lineItems['count']);
1147 $this->assertEquals($contribution['id'], $lineItems['values'][0]['entity_id']);
1148 $this->assertEquals($contribution['id'], $lineItems['values'][0]['contribution_id']);
1149 $lineItems = $this->callAPISuccess('line_item', 'get', [
1151 'entity_id' => $contribution['id'],
1152 'contribution_id' => $contribution['id'],
1153 'entity_table' => 'civicrm_contribution',
1156 $this->assertEquals(1, $lineItems['count']);
1157 $this->_checkFinancialRecords($contribution, 'feeAmount');
1161 * Function tests that additional financial records are created when online
1162 * contribution is created.
1164 * @throws \CRM_Core_Exception
1165 * @throws \CiviCRM_API3_Exception
1167 public function testCreateContributionOnline(): void
{
1168 CRM_Financial_BAO_PaymentProcessor
::create($this->_processorParams
);
1169 $contributionPage = $this->callAPISuccess('contribution_page', 'create', $this->_pageParams
);
1170 $this->assertAPISuccess($contributionPage);
1172 'contact_id' => $this->_individualId
,
1173 'receive_date' => '20120511',
1174 'total_amount' => 100.00,
1175 'financial_type_id' => 1,
1176 'contribution_page_id' => $contributionPage['id'],
1177 'payment_processor' => $this->paymentProcessorID
,
1179 'invoice_id' => 67890,
1181 'contribution_status_id' => 1,
1185 $contribution = $this->callAPISuccess('contribution', 'create', $params);
1186 $this->assertEquals($contribution['values'][$contribution['id']]['contact_id'], $this->_individualId
);
1187 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 100.00);
1188 $this->assertEquals($contribution['values'][$contribution['id']]['financial_type_id'], 1);
1189 $this->assertEquals($contribution['values'][$contribution['id']]['trxn_id'], 12345);
1190 $this->assertEquals($contribution['values'][$contribution['id']]['invoice_id'], 67890);
1191 $this->assertEquals($contribution['values'][$contribution['id']]['source'], 'SSF');
1192 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status_id'], 1);
1193 $contribution['payment_instrument_id'] = $this->callAPISuccessGetValue('PaymentProcessor', [
1194 'id' => $this->paymentProcessorID
,
1195 'return' => 'payment_instrument_id',
1197 $this->_checkFinancialRecords($contribution, 'online');
1201 * Check handling of financial type.
1203 * In the interests of removing financial type / contribution type checks from
1204 * legacy format function lets test that the api is doing this for us
1206 public function testCreateInvalidFinancialType() {
1207 $params = $this->_params
;
1208 $params['financial_type_id'] = 99999;
1209 $this->callAPIFailure($this->entity
, 'create', $params, "'99999' is not a valid option for field financial_type_id");
1213 * Check handling of financial type.
1215 * In the interests of removing financial type / contribution type checks from
1216 * legacy format function lets test that the api is doing this for us
1218 * @throws \CRM_Core_Exception
1220 public function testValidNamedFinancialType() {
1221 $params = $this->_params
;
1222 $params['financial_type_id'] = 'Donation';
1223 $this->callAPISuccess($this->entity
, 'create', $params);
1227 * Tests that additional financial records are created.
1229 * Checks when online contribution with pay later option is created
1231 * @throws \CRM_Core_Exception
1233 public function testCreateContributionPayLaterOnline() {
1234 $this->_pageParams
['is_pay_later'] = 1;
1235 $contributionPage = $this->callAPISuccess('contribution_page', 'create', $this->_pageParams
);
1236 $this->assertAPISuccess($contributionPage);
1238 'contact_id' => $this->_individualId
,
1239 'receive_date' => '20120511',
1240 'total_amount' => 100.00,
1241 'financial_type_id' => 1,
1242 'contribution_page_id' => $contributionPage['id'],
1244 'is_pay_later' => 1,
1245 'invoice_id' => 67890,
1247 'contribution_status_id' => 'Pending',
1251 $contribution = $this->callAPIAndDocument('Contribution', 'create', $params, __FUNCTION__
, __FILE__
);
1252 $contribution = $contribution['values'][$contribution['id']];
1253 $this->assertEquals($contribution['contact_id'], $this->_individualId
);
1254 $this->assertEquals($contribution['total_amount'], 100.00);
1255 $this->assertEquals($contribution['financial_type_id'], 1);
1256 $this->assertEquals($contribution['trxn_id'], 12345);
1257 $this->assertEquals($contribution['invoice_id'], 67890);
1258 $this->assertEquals($contribution['source'], 'SSF');
1259 $this->assertEquals($contribution['contribution_status_id'], 2);
1260 $this->_checkFinancialRecords($contribution, 'payLater');
1264 * Function tests that additional financial records are created for online contribution with pending option.
1266 public function testCreateContributionPendingOnline() {
1267 CRM_Financial_BAO_PaymentProcessor
::create($this->_processorParams
);
1268 $contributionPage = $this->callAPISuccess('contribution_page', 'create', $this->_pageParams
);
1269 $this->assertAPISuccess($contributionPage);
1271 'contact_id' => $this->_individualId
,
1272 'receive_date' => '20120511',
1273 'total_amount' => 100.00,
1274 'financial_type_id' => 1,
1275 'contribution_page_id' => $contributionPage['id'],
1277 'invoice_id' => 67890,
1279 'contribution_status_id' => 2,
1282 $contribution = $this->callAPISuccess('contribution', 'create', $params);
1283 $this->assertEquals($contribution['values'][$contribution['id']]['contact_id'], $this->_individualId
);
1284 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 100.00);
1285 $this->assertEquals($contribution['values'][$contribution['id']]['financial_type_id'], 1);
1286 $this->assertEquals($contribution['values'][$contribution['id']]['trxn_id'], 12345);
1287 $this->assertEquals($contribution['values'][$contribution['id']]['invoice_id'], 67890);
1288 $this->assertEquals($contribution['values'][$contribution['id']]['source'], 'SSF');
1289 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status_id'], 2);
1290 $this->_checkFinancialRecords($contribution, 'pending');
1294 * Test that BAO defaults work.
1296 * @throws \CRM_Core_Exception
1298 public function testCreateBAODefaults() {
1299 unset($this->_params
['contribution_source_id'], $this->_params
['payment_instrument_id']);
1300 $contribution = $this->callAPISuccess('Contribution', 'create', $this->_params
);
1301 $contribution = $this->callAPISuccess('Contribution', 'getsingle', [
1302 'id' => $contribution['id'],
1303 'api.contribution.delete' => 1,
1305 $this->assertEquals(1, $contribution['contribution_status_id']);
1306 $this->assertEquals('Check', $contribution['payment_instrument']);
1307 $this->callAPISuccessGetCount('Contribution', ['id' => $contribution['id']], 0);
1311 * Test that getsingle can be chained with delete.
1313 * @throws CRM_Core_Exception
1315 public function testDeleteChainedGetSingle() {
1316 $contribution = $this->callAPISuccess('Contribution', 'create', $this->_params
);
1317 $contribution = $this->callAPISuccess('Contribution', 'getsingle', [
1318 'id' => $contribution['id'],
1319 'api.contribution.delete' => 1,
1321 $this->callAPISuccessGetCount('Contribution', ['id' => $contribution['id']], 0);
1325 * Function tests that line items, financial records are updated when contribution amount is changed.
1327 * @throws \CRM_Core_Exception
1329 public function testCreateUpdateContributionChangeTotal() {
1330 $contribution = $this->callAPISuccess('contribution', 'create', $this->_params
);
1331 $lineItems = $this->callAPISuccess('line_item', 'getvalue', [
1333 'entity_id' => $contribution['id'],
1334 'entity_table' => 'civicrm_contribution',
1336 'return' => 'line_total',
1338 $this->assertEquals('100.00', $lineItems);
1339 $trxnAmount = $this->_getFinancialTrxnAmount($contribution['id']);
1340 // Financial trxn SUM = 100 + 5 (fee)
1341 $this->assertEquals('105.00', $trxnAmount);
1344 'id' => $contribution['id'],
1345 'total_amount' => '125',
1347 $contribution = $this->callAPISuccess('Contribution', 'create', $newParams);
1349 $lineItems = $this->callAPISuccess('line_item', 'getvalue', [
1351 'entity_id' => $contribution['id'],
1352 'entity_table' => 'civicrm_contribution',
1354 'return' => 'line_total',
1357 $this->assertEquals('125.00', $lineItems);
1358 $trxnAmount = $this->_getFinancialTrxnAmount($contribution['id']);
1360 // Financial trxn SUM = 125 + 5 (fee).
1361 $this->assertEquals('130.00', $trxnAmount);
1362 $this->assertEquals('125.00', $this->_getFinancialItemAmount($contribution['id']));
1366 * Function tests that line items, financial records are updated when pay later contribution is received.
1368 public function testCreateUpdateContributionPayLater() {
1370 'contact_id' => $this->_individualId
,
1371 'receive_date' => '2012-01-01',
1372 'total_amount' => 100.00,
1373 'financial_type_id' => $this->_financialTypeId
,
1374 'payment_instrument_id' => 1,
1375 'contribution_status_id' => 2,
1376 'is_pay_later' => 1,
1379 $contribution = $this->callAPISuccess('contribution', 'create', $contribParams);
1381 $newParams = array_merge($contribParams, [
1382 'id' => $contribution['id'],
1383 'contribution_status_id' => 1,
1385 $contribution = $this->callAPISuccess('contribution', 'create', $newParams);
1386 $contribution = $contribution['values'][$contribution['id']];
1387 $this->assertEquals($contribution['contribution_status_id'], '1');
1388 $this->_checkFinancialItem($contribution['id'], 'paylater');
1389 $this->_checkFinancialTrxn($contribution, 'payLater');
1393 * Function tests that financial records are updated when Payment Instrument is changed.
1395 public function testCreateUpdateContributionPaymentInstrument() {
1396 $instrumentId = $this->_addPaymentInstrument();
1398 'contact_id' => $this->_individualId
,
1399 'total_amount' => 100.00,
1400 'financial_type_id' => $this->_financialTypeId
,
1401 'payment_instrument_id' => 4,
1402 'contribution_status_id' => 1,
1405 $contribution = $this->callAPISuccess('contribution', 'create', $contribParams);
1407 $newParams = array_merge($contribParams, [
1408 'id' => $contribution['id'],
1409 'payment_instrument_id' => $instrumentId,
1411 $contribution = $this->callAPISuccess('contribution', 'create', $newParams);
1412 $this->assertAPISuccess($contribution);
1413 $this->checkFinancialTrxnPaymentInstrumentChange($contribution['id'], 4, $instrumentId);
1415 // cleanup - delete created payment instrument
1416 $this->_deletedAddedPaymentInstrument();
1420 * Function tests that financial records are updated when Payment Instrument is changed when amount is negative.
1422 public function testCreateUpdateNegativeContributionPaymentInstrument() {
1423 $instrumentId = $this->_addPaymentInstrument();
1425 'contact_id' => $this->_individualId
,
1426 'total_amount' => -100.00,
1427 'financial_type_id' => $this->_financialTypeId
,
1428 'payment_instrument_id' => 4,
1429 'contribution_status_id' => 1,
1432 $contribution = $this->callAPISuccess('contribution', 'create', $contribParams);
1434 $newParams = array_merge($contribParams, [
1435 'id' => $contribution['id'],
1436 'payment_instrument_id' => $instrumentId,
1438 $contribution = $this->callAPISuccess('contribution', 'create', $newParams);
1439 $this->assertAPISuccess($contribution);
1440 $this->checkFinancialTrxnPaymentInstrumentChange($contribution['id'], 4, $instrumentId, -100);
1442 // cleanup - delete created payment instrument
1443 $this->_deletedAddedPaymentInstrument();
1447 * Function tests that financial records are added when Contribution is Refunded.
1449 * @throws \CRM_Core_Exception
1451 public function testCreateUpdateContributionRefund() {
1452 $contributionParams = [
1453 'contact_id' => $this->_individualId
,
1454 'receive_date' => '2012-01-01',
1455 'total_amount' => 100.00,
1456 'financial_type_id' => $this->_financialTypeId
,
1457 'payment_instrument_id' => 4,
1458 'contribution_status_id' => 1,
1459 'trxn_id' => 'original_payment',
1461 $contribution = $this->callAPISuccess('Contribution', 'create', $contributionParams);
1462 $newParams = array_merge($contributionParams, [
1463 'id' => $contribution['id'],
1464 'contribution_status_id' => 'Refunded',
1465 'cancel_date' => '2015-01-01 09:00',
1466 'refund_trxn_id' => 'the refund',
1469 $contribution = $this->callAPISuccess('contribution', 'create', $newParams);
1470 $this->_checkFinancialTrxn($contribution, 'refund');
1471 $this->_checkFinancialItem($contribution['id'], 'refund');
1472 $this->assertEquals('original_payment', $this->callAPISuccessGetValue('Contribution', [
1473 'id' => $contribution['id'],
1474 'return' => 'trxn_id',
1479 * Refund a contribution for a financial type with a contra account.
1481 * CRM-17951 the contra account is a financial account with a relationship to a
1482 * financial type. It is not always configured but should be reflected
1483 * in the financial_trxn & financial_item table if it is.
1485 * @throws \CRM_Core_Exception
1487 public function testCreateUpdateChargebackContributionDefaultAccount() {
1488 $contribution = $this->callAPISuccess('Contribution', 'create', $this->_params
);
1489 $this->callAPISuccess('Contribution', 'create', [
1490 'id' => $contribution['id'],
1491 'contribution_status_id' => 'Chargeback',
1493 $this->callAPISuccessGetSingle('Contribution', ['contribution_status_id' => 'Chargeback']);
1495 $lineItems = $this->callAPISuccessGetSingle('LineItem', [
1496 'contribution_id' => $contribution['id'],
1497 'api.FinancialItem.getsingle' => ['amount' => ['<' => 0]],
1499 $this->assertEquals(1, $lineItems['api.FinancialItem.getsingle']['financial_account_id']);
1500 $this->callAPISuccessGetSingle('FinancialTrxn', [
1501 'total_amount' => -100,
1502 'status_id' => 'Chargeback',
1503 'to_financial_account_id' => 6,
1508 * Refund a contribution for a financial type with a contra account.
1510 * CRM-17951 the contra account is a financial account with a relationship to a
1511 * financial type. It is not always configured but should be reflected
1512 * in the financial_trxn & financial_item table if it is.
1514 * @throws \CRM_Core_Exception
1516 public function testCreateUpdateChargebackContributionCustomAccount() {
1517 $financialAccount = $this->callAPISuccess('FinancialAccount', 'create', [
1518 'name' => 'Chargeback Account',
1519 'is_active' => TRUE,
1522 $entityFinancialAccount = $this->callAPISuccess('EntityFinancialAccount', 'create', [
1523 'entity_id' => $this->_financialTypeId
,
1524 'entity_table' => 'civicrm_financial_type',
1525 'account_relationship' => 'Chargeback Account is',
1526 'financial_account_id' => 'Chargeback Account',
1529 $contribution = $this->callAPISuccess('Contribution', 'create', $this->_params
);
1530 $this->callAPISuccess('Contribution', 'create', [
1531 'id' => $contribution['id'],
1532 'contribution_status_id' => 'Chargeback',
1534 $this->callAPISuccessGetSingle('Contribution', ['contribution_status_id' => 'Chargeback']);
1536 $lineItems = $this->callAPISuccessGetSingle('LineItem', [
1537 'contribution_id' => $contribution['id'],
1538 'api.FinancialItem.getsingle' => ['amount' => ['<' => 0]],
1540 $this->assertEquals($financialAccount['id'], $lineItems['api.FinancialItem.getsingle']['financial_account_id']);
1542 $this->callAPISuccess('Contribution', 'delete', ['id' => $contribution['id']]);
1543 $this->callAPISuccess('EntityFinancialAccount', 'delete', ['id' => $entityFinancialAccount['id']]);
1544 $this->callAPISuccess('FinancialAccount', 'delete', ['id' => $financialAccount['id']]);
1548 * Refund a contribution for a financial type with a contra account.
1550 * CRM-17951 the contra account is a financial account with a relationship to a
1551 * financial type. It is not always configured but should be reflected
1552 * in the financial_trxn & financial_item table if it is.
1554 public function testCreateUpdateRefundContributionConfiguredContraAccount() {
1555 $financialAccount = $this->callAPISuccess('FinancialAccount', 'create', [
1556 'name' => 'Refund Account',
1557 'is_active' => TRUE,
1560 $entityFinancialAccount = $this->callAPISuccess('EntityFinancialAccount', 'create', [
1561 'entity_id' => $this->_financialTypeId
,
1562 'entity_table' => 'civicrm_financial_type',
1563 'account_relationship' => 'Credit/Contra Revenue Account is',
1564 'financial_account_id' => 'Refund Account',
1567 $contribution = $this->callAPISuccess('Contribution', 'create', $this->_params
);
1568 $this->callAPISuccess('Contribution', 'create', [
1569 'id' => $contribution['id'],
1570 'contribution_status_id' => 'Refunded',
1573 $lineItems = $this->callAPISuccessGetSingle('LineItem', [
1574 'contribution_id' => $contribution['id'],
1575 'api.FinancialItem.getsingle' => ['amount' => ['<' => 0]],
1577 $this->assertEquals($financialAccount['id'], $lineItems['api.FinancialItem.getsingle']['financial_account_id']);
1579 $this->callAPISuccess('Contribution', 'delete', ['id' => $contribution['id']]);
1580 $this->callAPISuccess('EntityFinancialAccount', 'delete', ['id' => $entityFinancialAccount['id']]);
1581 $this->callAPISuccess('FinancialAccount', 'delete', ['id' => $financialAccount['id']]);
1585 * Function tests that trxn_id is set when passed in.
1587 * Here we ensure that the civicrm_financial_trxn.trxn_id & the civicrm_contribution.trxn_id are set
1588 * when trxn_id is passed in.
1590 public function testCreateUpdateContributionRefundTrxnIDPassedIn() {
1591 $contributionParams = [
1592 'contact_id' => $this->_individualId
,
1593 'receive_date' => '2012-01-01',
1594 'total_amount' => 100.00,
1595 'financial_type_id' => $this->_financialTypeId
,
1596 'payment_instrument_id' => 4,
1597 'contribution_status_id' => 1,
1598 'trxn_id' => 'original_payment',
1600 $contribution = $this->callAPISuccess('contribution', 'create', $contributionParams);
1601 $newParams = array_merge($contributionParams, [
1602 'id' => $contribution['id'],
1603 'contribution_status_id' => 'Refunded',
1604 'cancel_date' => '2015-01-01 09:00',
1605 'trxn_id' => 'the refund',
1608 $contribution = $this->callAPISuccess('contribution', 'create', $newParams);
1609 $this->_checkFinancialTrxn($contribution, 'refund');
1610 $this->_checkFinancialItem($contribution['id'], 'refund');
1611 $this->assertEquals('the refund', $this->callAPISuccessGetValue('Contribution', [
1612 'id' => $contribution['id'],
1613 'return' => 'trxn_id',
1618 * Function tests that trxn_id is set when passed in.
1620 * Here we ensure that the civicrm_contribution.trxn_id is set
1621 * when trxn_id is passed in but if refund_trxn_id is different then that
1622 * is kept for the refund transaction.
1624 public function testCreateUpdateContributionRefundRefundAndTrxnIDPassedIn() {
1625 $contributionParams = [
1626 'contact_id' => $this->_individualId
,
1627 'receive_date' => '2012-01-01',
1628 'total_amount' => 100.00,
1629 'financial_type_id' => $this->_financialTypeId
,
1630 'payment_instrument_id' => 4,
1631 'contribution_status_id' => 1,
1632 'trxn_id' => 'original_payment',
1634 $contribution = $this->callAPISuccess('contribution', 'create', $contributionParams);
1635 $newParams = array_merge($contributionParams, [
1636 'id' => $contribution['id'],
1637 'contribution_status_id' => 'Refunded',
1638 'cancel_date' => '2015-01-01 09:00',
1639 'trxn_id' => 'cont id',
1640 'refund_trxn_id' => 'the refund',
1643 $contribution = $this->callAPISuccess('contribution', 'create', $newParams);
1644 $this->_checkFinancialTrxn($contribution, 'refund');
1645 $this->_checkFinancialItem($contribution['id'], 'refund');
1646 $this->assertEquals('cont id', $this->callAPISuccessGetValue('Contribution', [
1647 'id' => $contribution['id'],
1648 'return' => 'trxn_id',
1653 * Function tests that refund_trxn_id is set when passed in empty.
1655 * Here we ensure that the civicrm_contribution.trxn_id is set
1656 * when trxn_id is passed in but if refund_trxn_id isset but empty then that
1657 * is kept for the refund transaction.
1659 public function testCreateUpdateContributionRefundRefundNullTrxnIDPassedIn() {
1660 $contributionParams = [
1661 'contact_id' => $this->_individualId
,
1662 'receive_date' => '2012-01-01',
1663 'total_amount' => 100.00,
1664 'financial_type_id' => $this->_financialTypeId
,
1665 'payment_instrument_id' => 4,
1666 'contribution_status_id' => 1,
1667 'trxn_id' => 'original_payment',
1669 $contribution = $this->callAPISuccess('contribution', 'create', $contributionParams);
1670 $newParams = array_merge($contributionParams, [
1671 'id' => $contribution['id'],
1672 'contribution_status_id' => 'Refunded',
1673 'cancel_date' => '2015-01-01 09:00',
1674 'trxn_id' => 'cont id',
1675 'refund_trxn_id' => '',
1678 $contribution = $this->callAPISuccess('contribution', 'create', $newParams);
1679 $this->_checkFinancialTrxn($contribution, 'refund', NULL, ['trxn_id' => NULL]);
1680 $this->_checkFinancialItem($contribution['id'], 'refund');
1681 $this->assertEquals('cont id', $this->callAPISuccessGetValue('Contribution', [
1682 'id' => $contribution['id'],
1683 'return' => 'trxn_id',
1688 * Function tests invalid contribution status change.
1690 public function testCreateUpdateContributionInValidStatusChange() {
1693 'receive_date' => '2012-01-01',
1694 'total_amount' => 100.00,
1695 'financial_type_id' => 1,
1696 'payment_instrument_id' => 1,
1697 'contribution_status_id' => 1,
1699 $contribution = $this->callAPISuccess('contribution', 'create', $contribParams);
1700 $newParams = array_merge($contribParams, [
1701 'id' => $contribution['id'],
1702 'contribution_status_id' => 2,
1704 $this->callAPIFailure('contribution', 'create', $newParams, ts('Cannot change contribution status from Completed to Pending.'));
1709 * Function tests that financial records are added when Pending Contribution is Canceled.
1711 * @throws \CRM_Core_Exception
1713 public function testCreateUpdateContributionCancelPending() {
1715 'contact_id' => $this->_individualId
,
1716 'receive_date' => '2012-01-01',
1717 'total_amount' => 100.00,
1718 'financial_type_id' => $this->_financialTypeId
,
1719 'payment_instrument_id' => 1,
1720 'contribution_status_id' => 2,
1721 'is_pay_later' => 1,
1724 $contribution = $this->callAPISuccess('contribution', 'create', $contribParams);
1725 $newParams = array_merge($contribParams, [
1726 'id' => $contribution['id'],
1727 'contribution_status_id' => 3,
1728 'cancel_date' => '2012-02-02 09:00',
1730 //Check if trxn_date is same as cancel_date.
1732 'trxn_date' => '2012-02-02 09:00:00',
1734 $contribution = $this->callAPISuccess('contribution', 'create', $newParams);
1735 $this->_checkFinancialTrxn($contribution, 'cancelPending', NULL, $checkTrxnDate);
1736 $this->_checkFinancialItem($contribution['id'], 'cancelPending');
1740 * Function tests that financial records are added when Financial Type is Changed.
1742 * @throws \CRM_Core_Exception
1744 public function testCreateUpdateContributionChangeFinancialType() {
1746 'contact_id' => $this->_individualId
,
1747 'receive_date' => '2012-01-01',
1748 'total_amount' => 100.00,
1749 'financial_type_id' => 1,
1750 'payment_instrument_id' => 1,
1751 'contribution_status_id' => 1,
1754 $contribution = $this->callAPISuccess('contribution', 'create', $contribParams);
1755 $newParams = array_merge($contribParams, [
1756 'id' => $contribution['id'],
1757 'financial_type_id' => 3,
1759 $contribution = $this->callAPISuccess('contribution', 'create', $newParams);
1760 $this->_checkFinancialTrxn($contribution, 'changeFinancial');
1761 $this->_checkFinancialItem($contribution['id'], 'changeFinancial');
1765 * Function tets that financial records are correctly added when financial type is changed
1767 * @throws \CRM_Core_Exception
1769 public function testCreateUpdateContributionWithFeeAmountChangeFinancialType() {
1771 'contact_id' => $this->_individualId
,
1772 'receive_date' => '2012-01-01',
1773 'total_amount' => 100.00,
1774 'fee_amount' => 0.57,
1775 'financial_type_id' => 1,
1776 'payment_instrument_id' => 1,
1777 'contribution_status_id' => 1,
1780 $contribution = $this->callAPISuccess('contribution', 'create', $contribParams);
1781 $newParams = array_merge($contribParams, [
1782 'id' => $contribution['id'],
1783 'financial_type_id' => 3,
1785 $contribution = $this->callAPISuccess('contribution', 'create', $newParams);
1786 $this->_checkFinancialTrxn($contribution, 'changeFinancial', NULL, ['fee_amount' => '-0.57', 'net_amount' => '-99.43']);
1787 $this->_checkFinancialItem($contribution['id'], 'changeFinancial');
1791 * Test that update does not change status id CRM-15105.
1793 public function testCreateUpdateWithoutChangingPendingStatus() {
1794 $contribution = $this->callAPISuccess('contribution', 'create', array_merge($this->_params
, ['contribution_status_id' => 2]));
1795 $this->callAPISuccess('contribution', 'create', ['id' => $contribution['id'], 'source' => 'new source']);
1796 $contribution = $this->callAPISuccess('contribution', 'getsingle', [
1797 'id' => $contribution['id'],
1798 'api.contribution.delete' => 1,
1800 $this->assertEquals(2, $contribution['contribution_status_id']);
1804 * Test Updating a Contribution.
1806 * CHANGE: we require the API to do an incremental update
1808 public function testCreateUpdateContribution() {
1809 $contributionID = $this->contributionCreate([
1810 'contact_id' => $this->_individualId
,
1811 'trxn_id' => 212355,
1812 'financial_type_id' => $this->_financialTypeId
,
1813 'invoice_id' => 'old_invoice',
1816 'contribution_id' => $contributionID,
1818 $original = $this->callAPISuccess('contribution', 'get', $old_params);
1819 $this->assertEquals($original['id'], $contributionID);
1820 //set up list of old params, verify
1822 //This should not be required on update:
1823 $old_contact_id = $original['values'][$contributionID]['contact_id'];
1824 $old_payment_instrument = $original['values'][$contributionID]['instrument_id'];
1825 $old_fee_amount = $original['values'][$contributionID]['fee_amount'];
1826 $old_source = $original['values'][$contributionID]['contribution_source'];
1828 $old_trxn_id = $original['values'][$contributionID]['trxn_id'];
1829 $old_invoice_id = $original['values'][$contributionID]['invoice_id'];
1831 //check against values in CiviUnitTestCase::createContribution()
1832 $this->assertEquals($old_contact_id, $this->_individualId
);
1833 $this->assertEquals($old_fee_amount, 5.00);
1834 $this->assertEquals($old_source, 'SSF');
1835 $this->assertEquals($old_trxn_id, 212355);
1836 $this->assertEquals($old_invoice_id, 'old_invoice');
1838 'id' => $contributionID,
1839 'contact_id' => $this->_individualId
,
1840 'total_amount' => 105.00,
1841 'fee_amount' => 7.00,
1842 'financial_type_id' => $this->_financialTypeId
,
1843 'non_deductible_amount' => 22.00,
1844 'contribution_status_id' => 1,
1845 'note' => 'Donating for Noble Cause',
1848 $contribution = $this->callAPISuccess('contribution', 'create', $params);
1851 'contribution_id' => $contribution['id'],
1853 $contribution = $this->callAPISuccessGetSingle('contribution', $new_params);
1855 $this->assertEquals($contribution['contact_id'], $this->_individualId
);
1856 $this->assertEquals($contribution['total_amount'], 105.00);
1857 $this->assertEquals($contribution['financial_type_id'], $this->_financialTypeId
);
1858 $this->assertEquals($contribution['financial_type'], 'Donation');
1859 $this->assertEquals($contribution['instrument_id'], $old_payment_instrument);
1860 $this->assertEquals($contribution['non_deductible_amount'], 22.00);
1861 $this->assertEquals($contribution['fee_amount'], 7.00);
1862 $this->assertEquals($contribution['trxn_id'], $old_trxn_id);
1863 $this->assertEquals($contribution['invoice_id'], $old_invoice_id);
1864 $this->assertEquals($contribution['contribution_source'], $old_source);
1865 $this->assertEquals($contribution['contribution_status'], 'Completed');
1867 $this->assertEquals($contribution['net_amount'], $contribution['total_amount'] - $contribution['fee_amount']);
1870 'contribution_id' => $contributionID,
1872 $result = $this->callAPISuccess('contribution', 'delete', $params);
1873 $this->assertAPISuccess($result);
1877 * Check that net_amount is updated when a contribution is updated.
1879 * Update fee amount AND total amount, just fee amount, just total amount
1880 * and neither to check that net_amount is keep updated.
1882 public function testUpdateContributionNetAmountVariants() {
1883 $contributionID = $this->contributionCreate(['contact_id' => $this->individualCreate()]);
1885 $this->callAPISuccess('Contribution', 'create', [
1886 'id' => $contributionID,
1887 'total_amount' => 90,
1890 $contribution = $this->callAPISuccessGetSingle('Contribution', [
1891 'id' => $contributionID,
1892 'return' => ['net_amount', 'fee_amount', 'total_amount'],
1894 $this->assertEquals(6, $contribution['fee_amount']);
1895 $this->assertEquals(90, $contribution['total_amount']);
1896 $this->assertEquals(84, $contribution['net_amount']);
1898 $this->callAPISuccess('Contribution', 'create', [
1899 'id' => $contributionID,
1902 $contribution = $this->callAPISuccessGetSingle('Contribution', [
1903 'id' => $contributionID,
1904 'return' => ['net_amount', 'fee_amount', 'total_amount'],
1906 $this->assertEquals(3, $contribution['fee_amount']);
1907 $this->assertEquals(90, $contribution['total_amount']);
1908 $this->assertEquals(87, $contribution['net_amount']);
1910 $this->callAPISuccess('Contribution', 'create', [
1911 'id' => $contributionID,
1912 'total_amount' => 200,
1914 $contribution = $this->callAPISuccessGetSingle('Contribution', [
1915 'id' => $contributionID,
1916 'return' => ['net_amount', 'fee_amount', 'total_amount'],
1918 $this->assertEquals(3, $contribution['fee_amount']);
1919 $this->assertEquals(200, $contribution['total_amount']);
1920 $this->assertEquals(197, $contribution['net_amount']);
1922 $this->callAPISuccess('Contribution', 'create', [
1923 'id' => $contributionID,
1924 'payment_instrument' => 'Cash',
1926 $contribution = $this->callAPISuccessGetSingle('Contribution', [
1927 'id' => $contributionID,
1928 'return' => ['net_amount', 'fee_amount', 'total_amount'],
1930 $this->assertEquals(3, $contribution['fee_amount']);
1931 $this->assertEquals(200, $contribution['total_amount']);
1932 $this->assertEquals(197, $contribution['net_amount']);
1936 * Attempt (but fail) to delete a contribution without parameters.
1938 public function testDeleteEmptyParamsContribution() {
1940 $this->callAPIFailure('contribution', 'delete', $params);
1943 public function testDeleteWrongParamContribution() {
1945 'contribution_source' => 'SSF',
1947 $this->callAPIFailure('contribution', 'delete', $params);
1950 public function testDeleteContribution() {
1951 $contributionID = $this->contributionCreate([
1952 'contact_id' => $this->_individualId
,
1953 'financial_type_id' => $this->_financialTypeId
,
1956 'id' => $contributionID,
1958 $this->callAPIAndDocument('contribution', 'delete', $params, __FUNCTION__
, __FILE__
);
1962 * Test civicrm_contribution_search with empty params.
1964 * All available contributions expected.
1966 public function testSearchEmptyParams() {
1970 'contact_id' => $this->_individualId
,
1971 'receive_date' => date('Ymd'),
1972 'total_amount' => 100.00,
1973 'financial_type_id' => $this->_financialTypeId
,
1974 'non_deductible_amount' => 10.00,
1975 'fee_amount' => 5.00,
1976 'net_amount' => 95.00,
1978 'invoice_id' => 78910,
1980 'contribution_status_id' => 1,
1982 $contribution = $this->callAPISuccess('contribution', 'create', $p);
1984 $result = $this->callAPISuccess('contribution', 'get', $params);
1985 // We're taking the first element.
1986 $res = $result['values'][$contribution['id']];
1988 $this->assertEquals($p['contact_id'], $res['contact_id']);
1989 $this->assertEquals($p['total_amount'], $res['total_amount']);
1990 $this->assertEquals($p['financial_type_id'], $res['financial_type_id']);
1991 $this->assertEquals($p['net_amount'], $res['net_amount']);
1992 $this->assertEquals($p['non_deductible_amount'], $res['non_deductible_amount']);
1993 $this->assertEquals($p['fee_amount'], $res['fee_amount']);
1994 $this->assertEquals($p['trxn_id'], $res['trxn_id']);
1995 $this->assertEquals($p['invoice_id'], $res['invoice_id']);
1996 $this->assertEquals($p['source'], $res['contribution_source']);
1997 // contribution_status_id = 1 => Completed
1998 $this->assertEquals('Completed', $res['contribution_status']);
2000 $this->contributionDelete($contribution['id']);
2004 * Test civicrm_contribution_search. Success expected.
2006 public function testSearch() {
2008 'contact_id' => $this->_individualId
,
2009 'receive_date' => date('Ymd'),
2010 'total_amount' => 100.00,
2011 'financial_type_id' => $this->_financialTypeId
,
2012 'non_deductible_amount' => 10.00,
2013 'contribution_status_id' => 1,
2015 $contribution1 = $this->callAPISuccess('contribution', 'create', $p1);
2018 'contact_id' => $this->_individualId
,
2019 'receive_date' => date('Ymd'),
2020 'total_amount' => 200.00,
2021 'financial_type_id' => $this->_financialTypeId
,
2022 'non_deductible_amount' => 20.00,
2023 'trxn_id' => 5454565,
2024 'invoice_id' => 1212124,
2025 'fee_amount' => 50.00,
2026 'net_amount' => 60.00,
2027 'contribution_status_id' => 2,
2029 $contribution2 = $this->callAPISuccess('contribution', 'create', $p2);
2032 'contribution_id' => $contribution2['id'],
2034 $result = $this->callAPISuccess('contribution', 'get', $params);
2035 $res = $result['values'][$contribution2['id']];
2037 $this->assertEquals($p2['contact_id'], $res['contact_id']);
2038 $this->assertEquals($p2['total_amount'], $res['total_amount']);
2039 $this->assertEquals($p2['financial_type_id'], $res['financial_type_id']);
2040 $this->assertEquals($p2['net_amount'], $res['net_amount']);
2041 $this->assertEquals($p2['non_deductible_amount'], $res['non_deductible_amount']);
2042 $this->assertEquals($p2['fee_amount'], $res['fee_amount']);
2043 $this->assertEquals($p2['trxn_id'], $res['trxn_id']);
2044 $this->assertEquals($p2['invoice_id'], $res['invoice_id']);
2045 $this->assertEquals(CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'), $res['contribution_status_id']);
2047 $this->contributionDelete($contribution1['id']);
2048 $this->contributionDelete($contribution2['id']);
2052 * Test completing a transaction via the API.
2054 * Note that we are creating a logged in user because email goes out from
2057 public function testCompleteTransaction() {
2058 $mut = new CiviMailUtils($this, TRUE);
2059 $this->swapMessageTemplateForTestTemplate();
2060 $this->createLoggedInUser();
2061 $params = array_merge($this->_params
, ['contribution_status_id' => 2]);
2062 $contribution = $this->callAPISuccess('contribution', 'create', $params);
2063 $this->callAPISuccess('contribution', 'completetransaction', [
2064 'id' => $contribution['id'],
2066 $contribution = $this->callAPISuccess('contribution', 'getsingle', ['id' => $contribution['id']]);
2067 $this->assertEquals('SSF', $contribution['contribution_source']);
2068 $this->assertEquals('Completed', $contribution['contribution_status']);
2069 $this->assertEquals(date('Y-m-d'), date('Y-m-d', strtotime($contribution['receipt_date'])));
2070 $mut->checkMailLog([
2071 'email:::anthony_anderson@civicrm.org',
2075 'receive_date:::' . date('Ymd', strtotime($contribution['receive_date'])),
2076 "receipt_date:::\n",
2077 'contributeMode:::notify',
2078 'title:::Contribution',
2079 'displayName:::Mr. Anthony Anderson II',
2080 'contributionStatus:::Completed',
2083 $this->revertTemplateToReservedTemplate();
2087 * Test completing a transaction via the API with a non-USD transaction.
2089 public function testCompleteTransactionEuro() {
2090 $mut = new CiviMailUtils($this, TRUE);
2091 $this->swapMessageTemplateForTestTemplate();
2092 $this->createLoggedInUser();
2093 $params = array_merge($this->_params
, ['contribution_status_id' => 2, 'currency' => 'EUR']);
2094 $contribution = $this->callAPISuccess('contribution', 'create', $params);
2096 $this->callAPISuccess('contribution', 'completetransaction', [
2097 'id' => $contribution['id'],
2100 $contribution = $this->callAPISuccess('contribution', 'getsingle', ['id' => $contribution['id']]);
2101 $this->assertEquals('SSF', $contribution['contribution_source']);
2102 $this->assertEquals('Completed', $contribution['contribution_status']);
2103 $this->assertEquals(date('Y-m-d'), date('Y-m-d', strtotime($contribution['receipt_date'])));
2105 $entityFinancialTransactions = $this->getFinancialTransactionsForContribution($contribution['id']);
2106 $entityFinancialTransaction = reset($entityFinancialTransactions);
2107 $financialTrxn = $this->callAPISuccessGetSingle('FinancialTrxn', ['id' => $entityFinancialTransaction['financial_trxn_id']]);
2108 $this->assertEquals('EUR', $financialTrxn['currency']);
2110 $mut->checkMailLog([
2111 'email:::anthony_anderson@civicrm.org',
2115 'receive_date:::' . date('Ymd', strtotime($contribution['receive_date'])),
2116 "receipt_date:::\n",
2117 'contributeMode:::notify',
2118 'title:::Contribution',
2119 'displayName:::Mr. Anthony Anderson II',
2120 'contributionStatus:::Completed',
2123 $this->revertTemplateToReservedTemplate();
2127 * Test to ensure mail is sent for pay later
2129 * @throws \CRM_Core_Exception
2130 * @throws \API_Exception
2132 public function testPayLater(): void
{
2133 $mut = new CiviMailUtils($this, TRUE);
2134 $this->swapMessageTemplateForTestTemplate();
2135 $this->createLoggedInUser();
2136 $contributionPageID = $this->createQuickConfigContributionPage();
2139 'id' => $contributionPageID,
2140 'price_' . $this->ids
['PriceField']['basic'] => $this->ids
['PriceFieldValue']['basic'],
2141 'contact_id' => $this->_individualId
,
2142 'email-5' => 'anthony_anderson@civicrm.org',
2143 'payment_processor_id' => 0,
2144 'currencyID' => 'USD',
2145 'is_pay_later' => 1,
2146 'invoiceID' => 'f28e1ddc86f8c4a0ff5bcf46393e4bc8',
2147 'description' => 'Online Contribution: Help Support CiviCRM!',
2149 $this->callAPISuccess('ContributionPage', 'submit', $params);
2151 $mut->checkMailLog([
2153 'email:::anthony_anderson@civicrm.org',
2154 'pay_later_receipt:::This is a pay later receipt',
2155 'displayName:::Mr. Anthony Anderson II',
2156 'contributionPageId:::' . $contributionPageID,
2157 'title:::Test Contribution Page',
2161 $this->revertTemplateToReservedTemplate();
2165 * Test to check whether contact billing address is used when no contribution address
2167 public function testBillingAddress() {
2168 $mut = new CiviMailUtils($this, TRUE);
2169 $this->swapMessageTemplateForTestTemplate();
2170 $this->createLoggedInUser();
2172 //Scenario 1: When Contact don't have any address
2173 $params = array_merge($this->_params
, ['contribution_status_id' => 2]);
2174 $contribution = $this->callAPISuccess('contribution', 'create', $params);
2175 $this->callAPISuccess('contribution', 'completetransaction', [
2176 'id' => $contribution['id'],
2178 $mut->checkMailLog([
2182 // Scenario 2: Contribution using address
2183 $address = $this->callAPISuccess('address', 'create', [
2184 'street_address' => 'contribution billing st',
2185 'location_type_id' => 2,
2186 'contact_id' => $this->_params
['contact_id'],
2188 $params = array_merge($this->_params
, [
2189 'contribution_status_id' => 2,
2190 'address_id' => $address['id'],
2193 $contribution = $this->callAPISuccess('contribution', 'create', $params);
2194 $this->callAPISuccess('contribution', 'completetransaction', [
2195 'id' => $contribution['id'],
2197 $mut->checkMailLog([
2198 'address:::contribution billing st',
2201 // Scenario 3: Contribution wtth no address but contact has a billing address
2202 $this->callAPISuccess('address', 'create', [
2203 'id' => $address['id'],
2204 'street_address' => 'is billing st',
2205 'contact_id' => $this->_params
['contact_id'],
2207 $params = array_merge($this->_params
, ['contribution_status_id' => 2]);
2208 $contribution = $this->callAPISuccess('contribution', 'create', $params);
2209 $this->callAPISuccess('contribution', 'completetransaction', [
2210 'id' => $contribution['id'],
2212 $mut->checkMailLog([
2213 'address:::is billing st',
2217 $this->revertTemplateToReservedTemplate();
2221 * Test completing a transaction via the API.
2223 * Note that we are creating a logged in user because email goes out from
2226 public function testCompleteTransactionFeeAmount() {
2227 $this->createLoggedInUser();
2228 $params = array_merge($this->_params
, ['contribution_status_id' => 2]);
2229 $contribution = $this->callAPISuccess('contribution', 'create', $params);
2230 $this->callAPISuccess('contribution', 'completetransaction', [
2231 'id' => $contribution['id'],
2232 'fee_amount' => '.56',
2233 'trxn_id' => '7778888',
2235 $contribution = $this->callAPISuccess('contribution', 'getsingle', ['id' => $contribution['id'], 'sequential' => 1]);
2236 $this->assertEquals('Completed', $contribution['contribution_status']);
2237 $this->assertEquals('7778888', $contribution['trxn_id']);
2238 $this->assertEquals('0.56', $contribution['fee_amount']);
2239 $this->assertEquals('99.44', $contribution['net_amount']);
2243 * CRM-19126 Add test to verify when complete transaction is called tax
2244 * amount is not changed.
2246 * We start of with a pending contribution.
2247 * - total_amount (input) = 100
2248 * - total_amount post save (based on tax being added) = 105
2251 * - non_deductible_amount = 10
2254 * - sum of (calculated) line items = 105
2256 * Note the fee_amount should really be set when the payment is received
2257 * and whatever the non_deductible amount is, it is ignored.
2259 * The fee amount when the payment comes in is 6 rather than 5. The net_amount
2260 * and fee_amount should change, but not the total_amount or
2263 * @param string $thousandSeparator
2264 * punctuation used to refer to thousands.
2266 * @dataProvider getThousandSeparators
2267 * @throws \CRM_Core_Exception
2269 public function testCheckTaxAmount(string $thousandSeparator): void
{
2270 $this->setCurrencySeparators($thousandSeparator);
2271 $this->createFinancialTypeWithSalesTax();
2272 $financialTypeId = $this->ids
['FinancialType']['taxable'];
2274 $contributionID = $this->callAPISuccess('Order', 'create',
2275 array_merge($this->_params
, ['financial_type_id' => $financialTypeId])
2277 $contributionPrePayment = $this->callAPISuccessGetSingle('Contribution', ['id' => $contributionID, 'return' => ['tax_amount', 'total_amount']]);
2278 $this->validateAllContributions();
2279 $this->callAPISuccess('Contribution', 'completetransaction', [
2280 'id' => $contributionID,
2281 'trxn_id' => '777788888',
2282 'fee_amount' => '6.00',
2285 $contributionPostPayment = $this->callAPISuccessGetSingle('Contribution', ['id' => $contributionID, 'return' => ['tax_amount', 'fee_amount', 'net_amount']]);
2286 $this->assertEquals(5, $contributionPrePayment['tax_amount']);
2287 $this->assertEquals(5, $contributionPostPayment['tax_amount']);
2288 $this->assertEquals('6.00', $contributionPostPayment['fee_amount']);
2289 $this->assertEquals('99.00', $contributionPostPayment['net_amount']);
2290 $this->validateAllContributions();
2291 $this->validateAllPayments();
2295 * Test repeat contribution successfully creates line item.
2297 * @throws \CRM_Core_Exception
2299 public function testRepeatTransaction(): void
{
2300 $originalContribution = $this->setUpRepeatTransaction([], 'single');
2301 $this->callAPISuccess('contribution', 'repeattransaction', [
2302 'original_contribution_id' => $originalContribution['id'],
2303 'contribution_status_id' => 'Completed',
2307 'entity_id' => $originalContribution['id'],
2315 'financial_type_id',
2316 'deductible_amount',
2317 'price_field_value_id',
2321 $lineItem1 = $this->callAPISuccess('line_item', 'get', array_merge($lineItemParams, [
2322 'entity_id' => $originalContribution['id'],
2324 $lineItem2 = $this->callAPISuccess('line_item', 'get', array_merge($lineItemParams, [
2325 'entity_id' => $originalContribution['id'] +
1,
2327 unset($lineItem1['values'][0]['id'], $lineItem1['values'][0]['entity_id']);
2328 unset($lineItem2['values'][0]['id'], $lineItem2['values'][0]['entity_id']);
2329 $this->assertEquals($lineItem1['values'][0], $lineItem2['values'][0]);
2330 $this->_checkFinancialRecords([
2331 'id' => $originalContribution['id'] +
1,
2332 'payment_instrument_id' => $this->callAPISuccessGetValue('PaymentProcessor', [
2333 'id' => $originalContribution['payment_processor_id'],
2334 'return' => 'payment_instrument_id',
2340 * Test custom data is copied over from the template transaction.
2342 * (Over time various discussions have deemed this to be the most recent one, allowing
2343 * users to alter custom data going forwards. This is implemented for line items already.
2345 * @throws \API_Exception
2346 * @throws \CRM_Core_Exception
2348 public function testRepeatTransactionWithCustomData(): void
{
2349 $this->createCustomGroupWithFieldOfType(['extends' => 'Contribution', 'name' => 'Repeat'], 'text');
2350 $originalContribution = $this->setUpRepeatTransaction([], 'single', [$this->getCustomFieldName('text') => 'first']);
2351 $this->callAPISuccess('contribution', 'repeattransaction', [
2352 'contribution_recur_id' => $originalContribution['contribution_recur_id'],
2353 'contribution_status_id' => 'Completed',
2354 'trxn_id' => 'my_trxn',
2357 $contribution = Contribution
::get()
2358 ->addWhere('trxn_id', '=', 'my_trxn')
2359 ->addSelect('Custom_Group.Enter_text_here')
2361 ->execute()->first();
2362 $this->assertEquals('first', $contribution['Custom_Group.Enter_text_here']);
2364 Contribution
::update()->setValues(['Custom_Group.Enter_text_here' => 'second'])->addWhere('id', '=', $contribution['id'])->execute();
2366 $this->callAPISuccess('contribution', 'repeattransaction', [
2367 'original_contribution_id' => $originalContribution['id'],
2368 'contribution_status_id' => 'Completed',
2369 'trxn_id' => 'number_3',
2372 $contribution = Contribution
::get()
2373 ->addWhere('trxn_id', '=', 'number_3')
2374 ->setSelect(['id', 'Custom_Group.Enter_text_here'])
2375 ->execute()->first();
2376 $this->assertEquals('second', $contribution['Custom_Group.Enter_text_here']);
2380 * Test repeat contribution successfully creates line items (plural).
2382 * @throws \CRM_Core_Exception
2384 public function testRepeatTransactionLineItems(): void
{
2385 // @todo - figure out why this test is not valid.
2386 $this->isValidateFinancialsOnPostAssert
= FALSE;
2388 $originalContribution = $this->setUpRepeatTransaction([], 'multiple');
2389 $this->callAPISuccess('contribution', 'repeattransaction', [
2390 'original_contribution_id' => $originalContribution['id'],
2391 'contribution_status_id' => 'Completed',
2396 'entity_id' => $originalContribution['id'],
2404 'financial_type_id',
2405 'deductible_amount',
2406 'price_field_value_id',
2410 $lineItem1 = $this->callAPISuccess('line_item', 'get', array_merge($lineItemParams, [
2411 'entity_id' => $originalContribution['id'],
2413 $lineItem2 = $this->callAPISuccess('line_item', 'get', array_merge($lineItemParams, [
2414 'entity_id' => $originalContribution['id'] +
1,
2417 // unset id and entity_id for all of them to be able to compare the lineItems:
2418 unset($lineItem1['values'][0]['id'], $lineItem1['values'][0]['entity_id']);
2419 unset($lineItem2['values'][0]['id'], $lineItem2['values'][0]['entity_id']);
2420 $this->assertEquals($lineItem1['values'][0], $lineItem2['values'][0]);
2422 unset($lineItem1['values'][1]['id'], $lineItem1['values'][1]['entity_id']);
2423 unset($lineItem2['values'][1]['id'], $lineItem2['values'][1]['entity_id']);
2424 $this->assertEquals($lineItem1['values'][1], $lineItem2['values'][1]);
2426 // CRM-19309 so in future we also want to:
2427 // check that financial_line_items have been created for entity_id 3 and 4;
2429 $this->callAPISuccessGetCount('FinancialItem', ['description' => 'Sales Tax', 'amount' => 0], 0);
2433 * Test repeat contribution successfully creates is_test transaction.
2435 * @throws \CRM_Core_Exception
2437 public function testRepeatTransactionIsTest(): void
{
2438 $this->_params
['is_test'] = 1;
2439 $originalContribution = $this->setUpRepeatTransaction(['is_test' => 1], 'single');
2441 $this->callAPISuccess('contribution', 'repeattransaction', [
2442 'original_contribution_id' => $originalContribution['id'],
2443 'contribution_status_id' => 'Completed',
2444 'trxn_id' => '1234',
2446 $this->callAPISuccessGetCount('Contribution', ['contribution_test' => 1], 2);
2450 * Test repeat contribution passed in status.
2452 * @throws \CRM_Core_Exception
2454 public function testRepeatTransactionPassedInStatus(): void
{
2455 $originalContribution = $this->setUpRepeatTransaction([], 'single');
2457 $this->callAPISuccess('contribution', 'repeattransaction', [
2458 'original_contribution_id' => $originalContribution['id'],
2459 'contribution_status_id' => 'Pending',
2462 $this->callAPISuccessGetCount('Contribution', ['contribution_status_id' => 2], 1);
2466 * Test repeat contribution accepts recur_id instead of
2467 * original_contribution_id.
2469 * @throws \CRM_Core_Exception
2471 public function testRepeatTransactionAcceptRecurID(): void
{
2472 $contributionRecur = $this->callAPISuccess('contribution_recur', 'create', [
2473 'contact_id' => $this->_individualId
,
2474 'installments' => '12',
2475 'frequency_interval' => '1',
2477 'contribution_status_id' => 1,
2478 'start_date' => '2012-01-01 00:00:00',
2479 'currency' => 'USD',
2480 'frequency_unit' => 'month',
2481 'payment_processor_id' => $this->paymentProcessorID
,
2483 $this->callAPISuccess('contribution', 'create', array_merge(
2485 ['contribution_recur_id' => $contributionRecur['id']])
2488 $this->callAPISuccess('contribution', 'repeattransaction', [
2489 'contribution_recur_id' => $contributionRecur['id'],
2490 'contribution_status_id' => 'Completed',
2497 * CRM-19873 Test repeattransaction if contribution_recur_id is a test.
2499 * @throws \CRM_Core_Exception
2501 public function testRepeatTransactionTestRecurId() {
2502 $contributionRecur = $this->callAPISuccess('ContributionRecur', 'create', [
2503 'contact_id' => $this->_individualId
,
2504 'frequency_interval' => '1',
2506 'contribution_status_id' => 1,
2507 'start_date' => '2017-01-01 00:00:00',
2508 'currency' => 'USD',
2509 'frequency_unit' => 'month',
2510 'payment_processor_id' => $this->paymentProcessorID
,
2513 $this->callAPISuccess('contribution', 'create', array_merge(
2516 'contribution_recur_id' => $contributionRecur['id'],
2521 $repeatedContribution = $this->callAPISuccess('contribution', 'repeattransaction', [
2522 'contribution_recur_id' => $contributionRecur['id'],
2523 'contribution_status_id' => 'Completed',
2524 'trxn_id' => 'magic_number',
2527 $this->assertEquals($contributionRecur['values'][1]['is_test'], $repeatedContribution['values'][2]['is_test']);
2531 * CRM-19945 Tests that Contribute.repeattransaction renews a membership when contribution status=Completed
2533 * @throws \CRM_Core_Exception
2535 public function testRepeatTransactionMembershipRenewCompletedContribution(): void
{
2536 [$originalContribution, $membership] = $this->setUpAutoRenewMembership();
2538 $this->callAPISuccess('Contribution', 'repeattransaction', [
2539 'contribution_recur_id' => $originalContribution['values'][1]['contribution_recur_id'],
2540 'contribution_status_id' => 'Failed',
2543 $this->callAPISuccess('membership', 'create', [
2544 'id' => $membership['id'],
2545 'end_date' => 'yesterday',
2546 'status_id' => 'Expired',
2549 $contribution = $this->callAPISuccess('Contribution', 'repeattransaction', [
2550 'contribution_recur_id' => $originalContribution['values'][1]['contribution_recur_id'],
2551 'contribution_status_id' => 'Completed',
2552 'trxn_id' => 'bobsled',
2555 $membershipStatusId = $this->callAPISuccess('membership', 'getvalue', [
2556 'id' => $membership['id'],
2557 'return' => 'status_id',
2560 $membership = $this->callAPISuccess('membership', 'get', [
2561 'id' => $membership['id'],
2564 $this->assertEquals('New', CRM_Core_PseudoConstant
::getName('CRM_Member_BAO_Membership', 'status_id', $membershipStatusId));
2566 $lineItem = $this->callAPISuccessGetSingle('LineItem', ['contribution_id' => $contribution['id']]);
2567 $this->assertEquals('civicrm_membership', $lineItem['entity_table']);
2568 $this->callAPISuccessGetCount('MembershipPayment', ['membership_id' => $membership['id']]);
2572 * This is one of those tests that locks in existing behaviour.
2574 * I feel like correct behaviour is arguable & has been discussed in the past. However, if the membership has
2575 * a date which says it should be expired then the result of repeattransaction is to push that date
2576 * to be one membership term from 'now' with status 'new'.
2578 public function testRepeattransactionRenewMembershipOldMembership() {
2579 $entities = $this->setUpAutoRenewMembership();
2580 $newStatusID = CRM_Core_PseudoConstant
::getKey('CRM_Member_BAO_Membership', 'status_id', 'New');
2581 $membership = $this->callAPISuccess('Membership', 'create', [
2582 'id' => $entities[1]['id'],
2583 'join_date' => '4 months ago',
2584 'start_date' => '3 months ago',
2585 'end_date' => '2 months ago',
2587 $membership = $membership['values'][$membership['id']];
2589 // This status does not appear to be calculated at all and is set to 'new'. Feels like a bug.
2590 $this->assertEquals($newStatusID, $membership['status_id']);
2592 // So it seems renewing this expired membership results in it's new status being current and it being pushed to a future date
2593 $this->callAPISuccess('Contribution', 'repeattransaction', ['original_contribution_id' => $entities[0]['id'], 'contribution_status_id' => 'Completed']);
2594 $membership = $this->callAPISuccessGetSingle('Membership', ['id' => $membership['id']]);
2595 // If this date calculation winds up being flakey the spirit of the test would be maintained by just checking
2596 // date is greater than today.
2597 $this->assertEquals(date('Y-m-d', strtotime('+ 1 month -1 day')), $membership['end_date']);
2598 $this->assertEquals($newStatusID, $membership['membership_type_id']);
2602 * CRM-19945 Tests that Contribute.repeattransaction DOES NOT renew a membership when contribution status=Failed
2604 * @dataProvider contributionStatusProvider
2606 * @throws \CRM_Core_Exception
2608 public function testRepeatTransactionMembershipRenewContributionNotCompleted($contributionStatus): void
{
2609 // Completed status should renew so we don't test that here
2610 // In Progress status was never actually intended to be available for contributions.
2611 // Partially paid is not valid.
2612 if (in_array($contributionStatus['name'], ['Completed', 'In Progress', 'Partially paid'])) {
2615 [$originalContribution, $membership] = $this->setUpAutoRenewMembership();
2617 $this->callAPISuccess('Contribution', 'repeattransaction', [
2618 'original_contribution_id' => $originalContribution['id'],
2619 'contribution_status_id' => 'Completed',
2622 $this->callAPISuccess('membership', 'create', [
2623 'id' => $membership['id'],
2624 'end_date' => 'yesterday',
2625 'status_id' => 'Expired',
2628 $contribution = $this->callAPISuccess('contribution', 'repeattransaction', [
2629 'contribution_recur_id' => $originalContribution['values'][1]['contribution_recur_id'],
2630 'contribution_status_id' => $contributionStatus['name'],
2631 'trxn_id' => 'bobsled',
2634 $updatedMembership = $this->callAPISuccess('membership', 'getsingle', ['id' => $membership['id']]);
2636 $dateTime = new DateTime('yesterday');
2637 $this->assertEquals($dateTime->format('Y-m-d'), $updatedMembership['end_date']);
2638 $this->assertEquals(CRM_Core_PseudoConstant
::getKey('CRM_Member_BAO_Membership', 'status_id', 'Expired'), $updatedMembership['status_id']);
2640 $lineItem = $this->callAPISuccessGetSingle('LineItem', ['contribution_id' => $contribution['id']]);
2641 $this->assertEquals('civicrm_membership', $lineItem['entity_table']);
2642 $this->callAPISuccessGetCount('MembershipPayment', ['membership_id' => $membership['id']]);
2646 * Dataprovider provides contribution status as [optionvalue=>contribution_status_name]
2647 * FIXME: buildOptions seems to die in CRM_Core_Config::_construct when in test mode.
2650 * @throws \CiviCRM_API3_Exception
2652 public function contributionStatusProvider() {
2653 $contributionStatuses = civicrm_api3('OptionValue', 'get', [
2654 'return' => ["id", "name"],
2655 'option_group_id' => "contribution_status",
2657 foreach ($contributionStatuses['values'] as $statusName) {
2658 $statuses[] = [$statusName];
2664 * CRM-16397 test appropriate action if total amount has changed for single
2667 * @throws \CRM_Core_Exception
2669 public function testRepeatTransactionAlteredAmount(): void
{
2670 $paymentProcessorID = $this->paymentProcessorCreate();
2671 $contributionRecur = $this->callAPISuccess('contribution_recur', 'create', [
2672 'contact_id' => $this->_individualId
,
2673 'installments' => '12',
2674 'frequency_interval' => '1',
2676 'contribution_status_id' => 1,
2677 'start_date' => '2012-01-01 00:00:00',
2678 'currency' => 'USD',
2679 'frequency_unit' => 'month',
2680 'payment_processor_id' => $paymentProcessorID,
2682 $originalContribution = $this->callAPISuccess('contribution', 'create', array_merge(
2685 'contribution_recur_id' => $contributionRecur['id'],
2689 $this->callAPISuccess('contribution', 'repeattransaction', [
2690 'original_contribution_id' => $originalContribution['id'],
2691 'contribution_status_id' => 'Completed',
2693 'total_amount' => '400',
2698 'entity_id' => $originalContribution['id'],
2706 'financial_type_id',
2707 'deductible_amount',
2708 'price_field_value_id',
2712 $this->callAPISuccessGetSingle('contribution', [
2713 'total_amount' => 400,
2715 'net_amount' => 350,
2717 $lineItem1 = $this->callAPISuccess('line_item', 'get', array_merge($lineItemParams, [
2718 'entity_id' => $originalContribution['id'],
2720 $expectedLineItem = array_merge(
2721 $lineItem1['values'][0], [
2722 'line_total' => '400.00',
2723 'unit_price' => '400.00',
2727 $lineItem2 = $this->callAPISuccess('line_item', 'get', array_merge($lineItemParams, [
2728 'entity_id' => $originalContribution['id'] +
1,
2731 unset($expectedLineItem['id'], $expectedLineItem['entity_id'], $lineItem2['values'][0]['id'], $lineItem2['values'][0]['entity_id']);
2732 $this->assertEquals($expectedLineItem, $lineItem2['values'][0]);
2736 * Test financial_type_id override behaviour with a single line item.
2738 * CRM-17718 a passed in financial_type_id is allowed to override the
2739 * original contribution where there is only one line item.
2741 * @throws \CRM_Core_Exception
2743 public function testRepeatTransactionPassedInFinancialType() {
2744 $originalContribution = $this->setUpRecurringContribution();
2746 $this->callAPISuccess('Contribution', 'repeattransaction', [
2747 'original_contribution_id' => $originalContribution['id'],
2748 'contribution_status_id' => 'Completed',
2750 'financial_type_id' => 2,
2753 'entity_id' => $originalContribution['id'],
2761 'financial_type_id',
2762 'deductible_amount',
2763 'price_field_value_id',
2768 $this->callAPISuccessGetSingle('Contribution', [
2769 'total_amount' => 100,
2770 'financial_type_id' => 2,
2772 $lineItem1 = $this->callAPISuccess('line_item', 'get', array_merge($lineItemParams, [
2773 'entity_id' => $originalContribution['id'],
2775 $expectedLineItem = array_merge(
2776 $lineItem1['values'][0], [
2777 'line_total' => '100.00',
2778 'unit_price' => '100.00',
2779 'financial_type_id' => 2,
2780 'contribution_type_id' => 2,
2783 $lineItem2 = $this->callAPISuccess('line_item', 'get', array_merge($lineItemParams, [
2784 'entity_id' => $originalContribution['id'] +
1,
2786 unset($expectedLineItem['id'], $expectedLineItem['entity_id']);
2787 unset($lineItem2['values'][0]['id'], $lineItem2['values'][0]['entity_id']);
2788 $this->assertEquals($expectedLineItem, $lineItem2['values'][0]);
2792 * Test Contribution with Order api.
2794 * @throws \CRM_Core_Exception|\CiviCRM_API3_Exception
2796 public function testContributionOrder() {
2797 $this->_contactID
= $this->individualCreate();
2798 $this->createContributionAndMembershipOrder();
2799 $contribution = $this->callAPISuccess('contribution', 'get')['values'][$this->ids
['Contribution'][0]];
2800 $this->assertEquals('Pending Label**', $contribution['contribution_status']);
2801 $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_contactID
]);
2803 $this->callAPISuccess('Payment', 'create', [
2804 'contribution_id' => $this->ids
['Contribution'][0],
2805 'payment_instrument_id' => 'Check',
2806 'total_amount' => 300,
2808 $contribution = $this->callAPISuccess('contribution', 'get')['values'][$this->ids
['Contribution'][0]];
2809 $this->assertEquals('Completed', $contribution['contribution_status']);
2811 $lineItem = $this->callAPISuccess('LineItem', 'get', [
2813 'contribution_id' => $this->ids
['Contribution'][0],
2815 $this->assertCount(2, $lineItem);
2816 $this->assertEquals($this->ids
['Contribution'][0], $lineItem[0]['entity_id']);
2817 $this->assertEquals('civicrm_contribution', $lineItem[0]['entity_table']);
2818 $this->assertEquals($this->ids
['Contribution'][0], $lineItem[0]['contribution_id']);
2819 $this->assertEquals($this->ids
['Contribution'][0], $lineItem[1]['contribution_id']);
2820 $this->assertEquals('100.00', $lineItem[0]['line_total']);
2821 $this->assertEquals('200.00', $lineItem[1]['line_total']);
2822 $this->assertEquals($membership['id'], $lineItem[1]['entity_id']);
2823 $this->assertEquals('civicrm_membership', $lineItem[1]['entity_table']);
2827 * Test financial_type_id override behaviour with a single line item.
2829 * CRM-17718 a passed in financial_type_id is not allowed to override the
2830 * original contribution where there is more than one line item.
2832 * @throws \CRM_Core_Exception
2834 public function testRepeatTransactionPassedInFinancialTypeTwoLineItems(): void
{
2835 $this->_params
= $this->getParticipantOrderParams();
2836 $originalContribution = $this->setUpRecurringContribution();
2838 $this->callAPISuccess('Contribution', 'repeattransaction', [
2839 'original_contribution_id' => $originalContribution['id'],
2840 'contribution_status_id' => 'Completed',
2841 'trxn_id' => 'repeat',
2842 'financial_type_id' => 2,
2845 // Retrieve the new contribution and note the financial type passed in has been ignored.
2846 $contribution = $this->callAPISuccessGetSingle('Contribution', [
2847 'trxn_id' => 'repeat',
2849 $this->assertEquals(4, $contribution['financial_type_id']);
2851 $lineItems = $this->callAPISuccess('line_item', 'get', [
2852 'entity_id' => $contribution['id'],
2854 foreach ($lineItems as $lineItem) {
2855 $this->assertNotEquals(2, $lineItem['financial_type_id']);
2860 * CRM-17718 test appropriate action if financial type has changed for single
2863 * @throws \CRM_Core_Exception
2865 public function testRepeatTransactionUpdatedFinancialType(): void
{
2866 $originalContribution = $this->setUpRecurringContribution([], ['financial_type_id' => 2]);
2868 $this->callAPISuccess('contribution', 'repeattransaction', [
2869 'contribution_recur_id' => $originalContribution['id'],
2870 'contribution_status_id' => 'Completed',
2874 'entity_id' => $originalContribution['id'],
2882 'financial_type_id',
2883 'deductible_amount',
2884 'price_field_value_id',
2889 $this->callAPISuccessGetSingle('contribution', [
2890 'total_amount' => 100,
2891 'financial_type_id' => 2,
2893 $lineItem1 = $this->callAPISuccess('line_item', 'get', array_merge($lineItemParams, [
2894 'entity_id' => $originalContribution['id'],
2896 $expectedLineItem = array_merge(
2897 $lineItem1['values'][0], [
2898 'line_total' => '100.00',
2899 'unit_price' => '100.00',
2900 'financial_type_id' => 2,
2901 'contribution_type_id' => 2,
2905 $lineItem2 = $this->callAPISuccess('line_item', 'get', array_merge($lineItemParams, [
2906 'entity_id' => $originalContribution['id'] +
1,
2908 unset($expectedLineItem['id'], $expectedLineItem['entity_id']);
2909 unset($lineItem2['values'][0]['id'], $lineItem2['values'][0]['entity_id']);
2910 $this->assertEquals($expectedLineItem, $lineItem2['values'][0]);
2914 * CRM-16397 test appropriate action if campaign has been passed in.
2916 public function testRepeatTransactionPassedInCampaign() {
2917 $paymentProcessorID = $this->paymentProcessorCreate();
2918 $campaignID = $this->campaignCreate();
2919 $campaignID2 = $this->campaignCreate();
2920 $contributionRecur = $this->callAPISuccess('contribution_recur', 'create', [
2921 'contact_id' => $this->_individualId
,
2922 'installments' => '12',
2923 'frequency_interval' => '1',
2925 'contribution_status_id' => 1,
2926 'start_date' => '2012-01-01 00:00:00',
2927 'currency' => 'USD',
2928 'frequency_unit' => 'month',
2929 'payment_processor_id' => $paymentProcessorID,
2931 $originalContribution = $this->callAPISuccess('contribution', 'create', array_merge(
2934 'contribution_recur_id' => $contributionRecur['id'],
2935 'campaign_id' => $campaignID,
2939 $this->callAPISuccess('contribution', 'repeattransaction', [
2940 'original_contribution_id' => $originalContribution['id'],
2941 'contribution_status_id' => 'Completed',
2943 'campaign_id' => $campaignID2,
2946 $this->callAPISuccessGetSingle('contribution', [
2947 'total_amount' => 100,
2948 'campaign_id' => $campaignID2,
2953 * CRM-17718 campaign stored on contribution recur gets priority.
2955 * This reflects the fact we permit people to update them.
2957 * @throws \CRM_Core_Exception
2959 public function testRepeatTransactionUpdatedCampaign(): void
{
2960 $paymentProcessorID = $this->paymentProcessorCreate();
2961 $campaignID = $this->campaignCreate();
2962 $campaignID2 = $this->campaignCreate();
2963 $contributionRecur = $this->callAPISuccess('contribution_recur', 'create', [
2964 'contact_id' => $this->_individualId
,
2965 'installments' => '12',
2966 'frequency_interval' => '1',
2968 'contribution_status_id' => 1,
2969 'start_date' => '2012-01-01 00:00:00',
2970 'currency' => 'USD',
2971 'frequency_unit' => 'month',
2972 'payment_processor_id' => $paymentProcessorID,
2973 'campaign_id' => $campaignID,
2975 $originalContribution = $this->callAPISuccess('contribution', 'create', array_merge(
2978 'contribution_recur_id' => $contributionRecur['id'],
2979 'campaign_id' => $campaignID2,
2983 $this->callAPISuccess('contribution', 'repeattransaction', [
2984 'original_contribution_id' => $originalContribution['id'],
2985 'contribution_status_id' => 'Completed',
2989 $this->callAPISuccessGetSingle('Contribution', [
2990 'total_amount' => 100,
2991 'campaign_id' => $campaignID,
2996 * CRM-20685 Repeattransaction produces incorrect Financial Type ID (in
2997 * specific circumstance) - if number of lineItems = 1.
2999 * This case happens when the line item & contribution do not have the same
3000 * type in his initiating transaction.
3002 * @throws \CRM_Core_Exception
3004 public function testRepeatTransactionUpdatedFinancialTypeAndNotEquals() {
3005 $originalContribution = $this->setUpRecurringContribution([], ['financial_type_id' => 2]);
3006 // This will made the trick to get the not equals behaviour.
3007 $this->callAPISuccess('line_item', 'create', ['id' => 1, 'financial_type_id' => 4]);
3008 $this->callAPISuccess('contribution', 'repeattransaction', [
3009 'contribution_recur_id' => $originalContribution['id'],
3010 'contribution_status_id' => 'Completed',
3014 'entity_id' => $originalContribution['id'],
3022 'financial_type_id',
3023 'deductible_amount',
3024 'price_field_value_id',
3028 $this->callAPISuccessGetSingle('contribution', [
3029 'total_amount' => 100,
3030 'financial_type_id' => 2,
3032 $lineItem1 = $this->callAPISuccess('line_item', 'get', array_merge($lineItemParams, [
3033 'entity_id' => $originalContribution['id'],
3035 $expectedLineItem = array_merge(
3036 $lineItem1['values'][0], [
3037 'line_total' => '100.00',
3038 'unit_price' => '100.00',
3039 'financial_type_id' => 4,
3040 'contribution_type_id' => 4,
3044 $lineItem2 = $this->callAPISuccess('line_item', 'get', array_merge($lineItemParams, [
3045 'entity_id' => $originalContribution['id'] +
1,
3047 $this->callAPISuccess('line_item', 'create', ['id' => 1, 'financial_type_id' => 1]);
3048 unset($expectedLineItem['id'], $expectedLineItem['entity_id']);
3049 unset($lineItem2['values'][0]['id'], $lineItem2['values'][0]['entity_id']);
3050 $this->assertEquals($expectedLineItem, $lineItem2['values'][0]);
3054 * Test completing a transaction does not 'mess' with net amount (CRM-15960).
3056 public function testCompleteTransactionNetAmountOK() {
3057 $this->createLoggedInUser();
3058 $params = array_merge($this->_params
, ['contribution_status_id' => 2]);
3059 unset($params['net_amount']);
3060 $contribution = $this->callAPISuccess('contribution', 'create', $params);
3061 $this->callAPISuccess('contribution', 'completetransaction', [
3062 'id' => $contribution['id'],
3064 $contribution = $this->callAPISuccess('contribution', 'getsingle', ['id' => $contribution['id']]);
3065 $this->assertEquals('Completed', $contribution['contribution_status']);
3066 $this->assertTrue(($contribution['total_amount'] - $contribution['net_amount']) == $contribution['fee_amount']);
3070 * CRM-14151 - Test completing a transaction via the API.
3072 public function testCompleteTransactionWithReceiptDateSet() {
3073 $this->swapMessageTemplateForTestTemplate();
3074 $mut = new CiviMailUtils($this, TRUE);
3075 $this->createLoggedInUser();
3076 $params = array_merge($this->_params
, ['contribution_status_id' => 2, 'receipt_date' => 'now']);
3077 $contribution = $this->callAPISuccess('contribution', 'create', $params);
3078 $this->callAPISuccess('contribution', 'completetransaction', ['id' => $contribution['id'], 'trxn_date' => date('Y-m-d')]);
3079 $contribution = $this->callAPISuccess('contribution', 'get', ['id' => $contribution['id'], 'sequential' => 1]);
3080 $this->assertEquals('Completed', $contribution['values'][0]['contribution_status']);
3081 // Make sure receive_date is original date and make sure payment date is today
3082 $this->assertEquals('2012-05-11', date('Y-m-d', strtotime($contribution['values'][0]['receive_date'])));
3083 $payment = $this->callAPISuccess('payment', 'get', ['contribution_id' => $contribution['id'], 'sequential' => 1]);
3084 $this->assertEquals(date('Y-m-d'), date('Y-m-d', strtotime($payment['values'][0]['trxn_date'])));
3085 $mut->checkMailLog([
3086 'Receipt - Contribution',
3087 'receipt_date:::' . date('Ymd'),
3090 $this->revertTemplateToReservedTemplate();
3094 * CRM-1960 - Test to ensure that completetransaction respects the is_email_receipt setting
3096 public function testCompleteTransactionWithEmailReceiptInput() {
3097 $contributionPage = $this->createReceiptableContributionPage();
3099 $this->_params
['contribution_page_id'] = $contributionPage['id'];
3100 $params = array_merge($this->_params
, ['contribution_status_id' => 2]);
3101 $contribution = $this->callAPISuccess('contribution', 'create', $params);
3102 // Complete the transaction overriding is_email_receipt to = FALSE
3103 $this->callAPISuccess('contribution', 'completetransaction', [
3104 'id' => $contribution['id'],
3105 'trxn_date' => date('2011-04-09'),
3106 'trxn_id' => 'kazam',
3107 'is_email_receipt' => 0,
3109 // Check if a receipt was issued
3110 $receipt_date = $this->callAPISuccess('Contribution', 'getvalue', ['id' => $contribution['id'], 'return' => 'receipt_date']);
3111 $this->assertEquals('', $receipt_date);
3115 * Test that $is_recur is assigned to the receipt.
3117 public function testCompleteTransactionForRecurring() {
3118 $this->mut
= new CiviMailUtils($this, TRUE);
3119 $this->swapMessageTemplateForTestTemplate();
3120 $recurring = $this->setUpRecurringContribution();
3121 $contributionPage = $this->createReceiptableContributionPage(['is_recur' => TRUE, 'recur_frequency_unit' => 'month', 'recur_interval' => 1]);
3123 $this->_params
['contribution_page_id'] = $contributionPage['id'];
3124 $this->_params
['contribution_recur_id'] = $recurring['id'];
3126 $contribution = $this->setUpForCompleteTransaction();
3128 $this->callAPISuccess('contribution', 'completetransaction', [
3129 'id' => $contribution['id'],
3130 'trxn_date' => date('2011-04-09'),
3131 'trxn_id' => 'kazam',
3132 'is_email_receipt' => 1,
3135 $this->mut
->checkMailLog([
3137 'cancelSubscriptionUrl:::' . CIVICRM_UF_BASEURL
,
3140 $this->revertTemplateToReservedTemplate();
3144 * CRM-19710 - Test to ensure that completetransaction respects the input for
3145 * is_email_receipt setting.
3147 * If passed in it will override the default from contribution page.
3149 * @throws \CRM_Core_Exception
3151 public function testCompleteTransactionWithEmailReceiptInputTrue(): void
{
3152 $mut = new CiviMailUtils($this, TRUE);
3153 $this->createLoggedInUser();
3154 $contributionPageParams = ['is_email_receipt' => 0];
3155 // Create a Contribution Page with is_email_receipt = FALSE
3156 $contributionPageID = $this->createQuickConfigContributionPage($contributionPageParams);
3157 $this->_params
['contribution_page_id'] = $contributionPageID;
3158 $params = array_merge($this->_params
, ['contribution_status_id' => 2, 'receipt_date' => 'now']);
3159 $contribution = $this->callAPISuccess('contribution', 'create', $params);
3160 // Complete the transaction overriding is_email_receipt to = TRUE
3161 $this->callAPISuccess('contribution', 'completetransaction', [
3162 'id' => $contribution['id'],
3163 'is_email_receipt' => 1,
3165 $mut->checkMailLog([
3166 'Contribution Information',
3172 * Complete the transaction using the template with all the possible.
3174 public function testCompleteTransactionWithTestTemplate() {
3175 $this->swapMessageTemplateForTestTemplate();
3176 $contribution = $this->setUpForCompleteTransaction();
3177 $this->callAPISuccess('contribution', 'completetransaction', [
3178 'id' => $contribution['id'],
3179 'trxn_date' => date('2011-04-09'),
3180 'trxn_id' => 'kazam',
3182 $receive_date = $this->callAPISuccess('Contribution', 'getvalue', ['id' => $contribution['id'], 'return' => 'receive_date']);
3183 $this->mut
->checkMailLog([
3184 'email:::anthony_anderson@civicrm.org',
3188 'receive_date:::' . date('Ymd', strtotime($receive_date)),
3189 'receipt_date:::' . date('Ymd'),
3190 'contributeMode:::notify',
3191 'title:::Contribution',
3192 'displayName:::Mr. Anthony Anderson II',
3194 'contactID:::' . $this->_params
['contact_id'],
3195 'contributionID:::' . $contribution['id'],
3196 'financialTypeId:::1',
3197 'financialTypeName:::Donation',
3200 $this->revertTemplateToReservedTemplate();
3204 * Complete the transaction using the template with all the possible.
3206 public function testCompleteTransactionContributionPageFromAddress() {
3207 $contributionPage = $this->callAPISuccess('ContributionPage', 'create', [
3208 'receipt_from_name' => 'Mickey Mouse',
3209 'receipt_from_email' => 'mickey@mouse.com',
3210 'title' => "Test Contribution Page",
3211 'financial_type_id' => 1,
3212 'currency' => 'NZD',
3213 'goal_amount' => 50,
3214 'is_pay_later' => 1,
3215 'is_monetary' => TRUE,
3216 'is_email_receipt' => TRUE,
3218 $this->_params
['contribution_page_id'] = $contributionPage['id'];
3219 $contribution = $this->setUpForCompleteTransaction();
3220 $this->callAPISuccess('contribution', 'completetransaction', ['id' => $contribution['id']]);
3221 $this->mut
->checkMailLog([
3229 * Test completing first transaction in a recurring series.
3231 * The status should be set to 'in progress' and the next scheduled payment date calculated.
3233 * @dataProvider getScheduledDateData
3235 * @param array $dataSet
3237 * @throws \Exception
3239 public function testCompleteTransactionSetStatusToInProgress($dataSet) {
3240 $paymentProcessorID = $this->paymentProcessorCreate();
3241 $contributionRecur = $this->callAPISuccess('contribution_recur', 'create', array_merge([
3242 'contact_id' => $this->_individualId
,
3243 'installments' => '2',
3244 'frequency_interval' => '1',
3246 'contribution_status_id' => 'Pending',
3247 'start_date' => '2012-01-01 00:00:00',
3248 'currency' => 'USD',
3249 'frequency_unit' => 'month',
3250 'payment_processor_id' => $paymentProcessorID,
3251 ], $dataSet['data']));
3252 $contribution = $this->callAPISuccess('contribution', 'create', array_merge(
3255 'contribution_recur_id' => $contributionRecur['id'],
3256 'contribution_status_id' => 'Pending',
3257 'receive_date' => $dataSet['receive_date'],
3260 $this->callAPISuccess('Contribution', 'completetransaction', [
3261 'id' => $contribution,
3262 'receive_date' => $dataSet['receive_date'],
3264 $contributionRecur = $this->callAPISuccessGetSingle('ContributionRecur', [
3265 'id' => $contributionRecur['id'],
3266 'return' => ['next_sched_contribution_date', 'contribution_status_id'],
3268 $this->assertEquals(5, $contributionRecur['contribution_status_id']);
3269 $this->assertEquals($dataSet['expected'], $contributionRecur['next_sched_contribution_date']);
3270 $this->callAPISuccess('Contribution', 'create', array_merge(
3273 'contribution_recur_id' => $contributionRecur['id'],
3274 'contribution_status_id' => 'Completed',
3277 $contributionRecur = $this->callAPISuccessGetSingle('ContributionRecur', [
3278 'id' => $contributionRecur['id'],
3279 'return' => ['contribution_status_id'],
3281 $this->assertEquals(1, $contributionRecur['contribution_status_id']);
3285 * Get dates for testing.
3289 public function getScheduledDateData() {
3291 $result[]['2016-08-31-1-month'] = [
3293 'start_date' => '2016-08-31',
3294 'frequency_interval' => 1,
3295 'frequency_unit' => 'month',
3297 'receive_date' => '2016-08-31',
3298 'expected' => '2016-10-01 00:00:00',
3300 $result[]['2012-01-01-1-month'] = [
3302 'start_date' => '2012-01-01',
3303 'frequency_interval' => 1,
3304 'frequency_unit' => 'month',
3306 'receive_date' => '2012-01-01',
3307 'expected' => '2012-02-01 00:00:00',
3309 $result[]['2012-01-01-1-month'] = [
3311 'start_date' => '2012-01-01',
3312 'frequency_interval' => 1,
3313 'frequency_unit' => 'month',
3315 'receive_date' => '2012-02-29',
3316 'expected' => '2012-03-29 00:00:00',
3318 $result['receive_date_includes_time']['2012-01-01-1-month'] = [
3320 'start_date' => '2012-01-01',
3321 'frequency_interval' => 1,
3322 'frequency_unit' => 'month',
3323 'next_sched_contribution_date' => '2012-02-29',
3325 'receive_date' => '2012-02-29 16:00:00',
3326 'expected' => '2012-03-29 00:00:00',
3332 * Test completing a pledge with the completeTransaction api..
3334 * Note that we are creating a logged in user because email goes out from
3337 public function testCompleteTransactionUpdatePledgePayment() {
3338 $this->swapMessageTemplateForTestTemplate();
3339 $mut = new CiviMailUtils($this, TRUE);
3340 $mut->clearMessages();
3341 $this->createLoggedInUser();
3342 $contributionID = $this->createPendingPledgeContribution();
3343 $this->callAPISuccess('contribution', 'completetransaction', [
3344 'id' => $contributionID,
3345 'trxn_date' => '1 Feb 2013',
3347 $pledge = $this->callAPISuccessGetSingle('Pledge', [
3348 'id' => $this->_ids
['pledge'],
3350 $this->assertEquals('Completed', $pledge['pledge_status']);
3352 $status = $this->callAPISuccessGetValue('PledgePayment', [
3353 'pledge_id' => $this->_ids
['pledge'],
3354 'return' => 'status_id',
3356 $this->assertEquals(1, $status);
3357 $mut->checkMailLog([
3359 // The `receive_date` should remain as it was created.
3360 // TODO: the latest payment transaction date (and maybe other details,
3361 // such as amount and payment instrument) would be a useful token to make
3363 'receive_date:::20120511000000',
3364 "receipt_date:::\n",
3367 $this->revertTemplateToReservedTemplate();
3371 * Test completing a transaction with an event via the API.
3373 * Note that we are creating a logged in user because email goes out from
3376 * @throws \CRM_Core_Exception
3378 public function testCompleteTransactionWithParticipantRecord(): void
{
3379 $mut = new CiviMailUtils($this, TRUE);
3380 $mut->clearMessages();
3381 $this->_individualId
= $this->createLoggedInUser();
3382 $this->_params
['source'] = 'Online Event Registration: Annual CiviCRM meet';
3383 $contributionID = $this->createPendingParticipantContribution();
3384 $this->createJoinedProfile(['entity_id' => $this->_ids
['event']['test'], 'entity_table' => 'civicrm_event']);
3385 $this->createJoinedProfile(['entity_id' => $this->_ids
['event']['test'], 'entity_table' => 'civicrm_event', 'weight' => 2], ['name' => 'post_1', 'title' => 'title_post_2', 'frontend_title' => 'public 2']);
3386 $this->createJoinedProfile(['entity_id' => $this->_ids
['event']['test'], 'entity_table' => 'civicrm_event', 'weight' => 3], ['name' => 'post_2', 'title' => 'title_post_3', 'frontend_title' => 'public 3']);
3387 $this->eliminateUFGroupOne();
3389 $this->callAPISuccess('contribution', 'completetransaction', ['id' => $contributionID]);
3390 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $contributionID, 'return' => ['contribution_source']]);
3391 $this->assertEquals('Online Event Registration: Annual CiviCRM meet', $contribution['contribution_source']);
3392 $participantStatus = $this->callAPISuccessGetValue('participant', [
3393 'id' => $this->_ids
['participant'],
3394 'return' => 'participant_status_id',
3396 $this->assertEquals(1, $participantStatus);
3398 //Assert only three activities are created.
3399 $activities = $this->callAPISuccess('Activity', 'get', [
3400 'contact_id' => $this->_individualId
,
3403 $this->assertCount(3, $activities);
3404 $activityNames = array_count_values(CRM_Utils_Array
::collect('activity_name', $activities));
3405 // record two activities before and after completing payment for Event registration
3406 $this->assertEquals(2, $activityNames['Event Registration']);
3407 // update the original 'Contribution' activity created after completing payment
3408 $this->assertEquals(1, $activityNames['Contribution']);
3410 $mut->checkMailLog([
3411 'Annual CiviCRM meet',
3413 'This is a confirmation that your registration has been received and your status has been updated to Registered.',
3414 'First Name: Logged In',
3418 ], ['Back end title', 'title_post_2', 'title_post_3']);
3423 * Test membership is renewed when transaction completed.
3425 public function testCompleteTransactionMembershipPriceSet() {
3426 $this->createPriceSetWithPage('membership');
3427 $stateOfGrace = $this->callAPISuccess('MembershipStatus', 'getvalue', [
3431 $this->setUpPendingContribution($this->_ids
['price_field_value'][0]);
3432 $membership = $this->callAPISuccess('membership', 'getsingle', ['id' => $this->_ids
['membership']]);
3433 $logs = $this->callAPISuccess('MembershipLog', 'get', [
3434 'membership_id' => $this->_ids
['membership'],
3436 $this->assertEquals(1, $logs['count']);
3437 $this->assertEquals($stateOfGrace, $membership['status_id']);
3438 $this->callAPISuccess('contribution', 'completetransaction', ['id' => $this->_ids
['contribution']]);
3439 $membership = $this->callAPISuccess('membership', 'getsingle', ['id' => $this->_ids
['membership']]);
3440 $this->assertEquals(date('Y-m-d', strtotime('yesterday + 1 year')), $membership['end_date']);
3441 $this->callAPISuccessGetSingle('LineItem', [
3442 'entity_id' => $this->_ids
['membership'],
3443 'entity_table' => 'civicrm_membership',
3445 $logs = $this->callAPISuccess('MembershipLog', 'get', [
3446 'membership_id' => $this->_ids
['membership'],
3448 $this->assertEquals(2, $logs['count']);
3449 $this->assertNotEquals($stateOfGrace, $logs['values'][2]['status_id']);
3450 //Assert only three activities are created.
3451 $activities = CRM_Activity_BAO_Activity
::getContactActivity($this->_ids
['contact']);
3452 $this->assertEquals(3, count($activities));
3453 $activityNames = array_flip(CRM_Utils_Array
::collect('activity_name', $activities));
3454 $this->assertArrayHasKey('Contribution', $activityNames);
3455 $this->assertArrayHasKey('Membership Signup', $activityNames);
3456 $this->assertArrayHasKey('Change Membership Status', $activityNames);
3457 $this->cleanUpAfterPriceSets();
3461 * Test if renewal activity is create after changing Pending contribution to
3462 * Completed via offline
3464 * @throws \CRM_Core_Exception
3465 * @throws \CRM_Core_Exception
3466 * @throws \CiviCRM_API3_Exception
3468 public function testPendingToCompleteContribution(): void
{
3469 // @todo - figure out why this test is not valid.
3470 $this->isValidateFinancialsOnPostAssert
= FALSE;
3471 $this->createPriceSetWithPage('membership');
3472 $this->setUpPendingContribution($this->_ids
['price_field_value'][0]);
3473 $this->callAPISuccess('membership', 'getsingle', ['id' => $this->_ids
['membership']]);
3474 // Case 1: Assert that Membership Signup Activity is created on Pending to Completed Contribution via backoffice
3475 $activity = $this->callAPISuccess('Activity', 'get', [
3476 'activity_type_id' => 'Membership Signup',
3477 'source_record_id' => $this->_ids
['membership'],
3478 'status_id' => 'Scheduled',
3480 $this->assertEquals(1, $activity['count']);
3482 // change pending contribution to completed
3483 $form = new CRM_Contribute_Form_Contribution();
3486 'id' => $this->_ids
['contribution'],
3487 'total_amount' => 20,
3490 'financial_type_id' => 1,
3491 'contact_id' => $this->_individualId
,
3492 'contribution_status_id' => 1,
3493 'billing_middle_name' => '',
3494 'billing_last_name' => 'Adams',
3495 'billing_street_address-5' => '790L Lincoln St S',
3496 'billing_city-5' => 'Maryknoll',
3497 'billing_state_province_id-5' => 1031,
3498 'billing_postal_code-5' => 10545,
3499 'billing_country_id-5' => 1228,
3500 'frequency_interval' => 1,
3501 'frequency_unit' => 'month',
3502 'installments' => '',
3503 'hidden_AdditionalDetail' => 1,
3504 'hidden_Premium' => 1,
3505 'from_email_address' => '"civi45" <civi45@civicrm.com>',
3506 'receipt_date' => '',
3507 'receipt_date_time' => '',
3508 'payment_processor_id' => $this->paymentProcessorID
,
3509 'currency' => 'USD',
3510 'contribution_page_id' => $this->_ids
['contribution_page'],
3511 'contribution_mode' => 'membership',
3512 'source' => 'Membership Signup and Renewal',
3515 $form->testSubmit($form->_params
, CRM_Core_Action
::UPDATE
);
3517 // Case 2: After successful payment for Pending backoffice there are three activities created
3518 // 2.a Update status of existing Scheduled Membership Signup (created in step 1) to Completed
3519 $activity = $this->callAPISuccess('Activity', 'get', [
3520 'activity_type_id' => 'Membership Signup',
3521 'source_record_id' => $this->_ids
['membership'],
3522 'status_id' => 'Completed',
3524 $this->assertEquals(1, $activity['count']);
3525 // 2.b Contribution activity created to record successful payment
3526 $activity = $this->callAPISuccess('Activity', 'get', [
3527 'activity_type_id' => 'Contribution',
3528 'source_record_id' => $this->_ids
['contribution'],
3529 'status_id' => 'Completed',
3531 $this->assertEquals(1, $activity['count']);
3533 // 2.c 'Change membership type' activity created to record Membership status change from Grace to Current
3534 $activity = $this->callAPISuccess('Activity', 'get', [
3535 'activity_type_id' => 'Change Membership Status',
3536 'source_record_id' => $this->_ids
['membership'],
3537 'status_id' => 'Completed',
3539 $this->assertEquals(1, $activity['count']);
3540 $this->assertEquals('Status changed from Grace to Current', $activity['values'][$activity['id']]['subject']);
3541 $membershipLogs = $this->callAPISuccess('MembershipLog', 'get', ['sequential' => 1])['values'];
3542 $this->assertEquals('Grace', CRM_Core_PseudoConstant
::getName('CRM_Member_BAO_Membership', 'status_id', $membershipLogs[0]['status_id']));
3543 $this->assertEquals('Current', CRM_Core_PseudoConstant
::getName('CRM_Member_BAO_Membership', 'status_id', $membershipLogs[1]['status_id']));
3544 //Create another pending contribution for renewal
3545 $contribution = $this->callAPISuccess('contribution', 'create', [
3547 'contact_id' => $this->_ids
['contact'],
3548 'receive_date' => date('Ymd'),
3549 'total_amount' => 20.00,
3550 'financial_type_id' => 1,
3551 'payment_instrument_id' => 'Credit Card',
3552 'non_deductible_amount' => 10.00,
3553 'trxn_id' => 'rdhfi88',
3554 'invoice_id' => 'dofhiewuyr',
3556 'contribution_status_id' => 2,
3557 'contribution_page_id' => $this->_ids
['contribution_page'],
3558 // We can't rely on contribution api to link line items correctly to membership
3559 'skipLineItem' => TRUE,
3560 'api.membership_payment.create' => ['membership_id' => $this->_ids
['membership']],
3563 $this->callAPISuccess('line_item', 'create', [
3564 'entity_id' => $contribution['id'],
3565 'entity_table' => 'civicrm_contribution',
3566 'contribution_id' => $contribution['id'],
3567 'price_field_id' => $this->_ids
['price_field'][0],
3571 'financial_type_id' => 1,
3572 'price_field_value_id' => $this->_ids
['price_field_value']['cont'],
3574 $this->callAPISuccess('line_item', 'create', [
3575 'entity_id' => $this->_ids
['membership'],
3576 'entity_table' => 'civicrm_membership',
3577 'contribution_id' => $contribution['id'],
3578 'price_field_id' => $this->_ids
['price_field'][0],
3582 'financial_type_id' => 1,
3583 'price_field_value_id' => $this->_ids
['price_field_value'][0],
3584 'membership_type_id' => $this->_ids
['membership_type'],
3587 //Update it to Failed.
3588 $form->_params
['id'] = $contribution['id'];
3589 $form->_params
['contribution_status_id'] = 4;
3591 $form->testSubmit($form->_params
, CRM_Core_Action
::UPDATE
);
3592 //Existing membership should not get updated to expired.
3593 $membership = $this->callAPISuccess('membership', 'getsingle', ['id' => $this->_ids
['membership']]);
3594 $this->assertNotEquals(4, $membership['status_id']);
3598 * Test membership is renewed for 2 terms when transaction completed based on the line item having 2 terms as qty.
3600 * Also check that altering the qty for the most recent contribution results in repeattransaction picking it up.
3602 public function testCompleteTransactionMembershipPriceSetTwoTerms() {
3603 $this->createPriceSetWithPage('membership');
3604 $this->setUpPendingContribution($this->_ids
['price_field_value'][1]);
3605 $this->callAPISuccess('contribution', 'completetransaction', ['id' => $this->_ids
['contribution']]);
3606 $membership = $this->callAPISuccessGetSingle('membership', ['id' => $this->_ids
['membership']]);
3607 $this->assertEquals(date('Y-m-d', strtotime('yesterday + 2 years')), $membership['end_date']);
3609 $paymentProcessorID = $this->paymentProcessorAuthorizeNetCreate();
3611 $contributionRecurID = $this->callAPISuccess('ContributionRecur', 'create', ['contact_id' => $membership['contact_id'], 'payment_processor_id' => $paymentProcessorID, 'amount' => 20, 'frequency_interval' => 1])['id'];
3612 $this->callAPISuccess('Contribution', 'create', ['id' => $this->_ids
['contribution'], 'contribution_recur_id' => $contributionRecurID]);
3613 $this->callAPISuccess('contribution', 'repeattransaction', ['contribution_recur_id' => $contributionRecurID, 'contribution_status_id' => 'Completed']);
3614 $membership = $this->callAPISuccessGetSingle('membership', ['id' => $this->_ids
['membership']]);
3615 $this->assertEquals(date('Y-m-d', strtotime('yesterday + 4 years')), $membership['end_date']);
3617 // Update the most recent contribution to have a qty of 1 in it's line item and then repeat, expecting just 1 year to be added.
3618 $contribution = Contribution
::get()->setOrderBy(['id' => 'DESC'])->setSelect(['id'])->execute()->first();
3619 CRM_Core_DAO
::executeQuery('UPDATE civicrm_line_item SET price_field_value_id = ' . $this->_ids
['price_field_value'][0] . ' WHERE contribution_id = ' . $contribution['id']);
3620 $this->callAPISuccess('contribution', 'repeattransaction', ['contribution_recur_id' => $contributionRecurID, 'contribution_status_id' => 'Completed']);
3621 $membership = $this->callAPISuccessGetSingle('membership', ['id' => $this->_ids
['membership']]);
3622 $this->assertEquals(date('Y-m-d', strtotime('yesterday + 5 years')), $membership['end_date']);
3624 $this->cleanUpAfterPriceSets();
3627 public function cleanUpAfterPriceSets() {
3628 $this->quickCleanUpFinancialEntities();
3629 $this->contactDelete($this->_ids
['contact']);
3633 * Set up a pending transaction with a specific price field id.
3635 * @param int $priceFieldValueID
3636 * @param array $contriParams
3638 * @throws \CRM_Core_Exception
3639 * @throws \CiviCRM_API3_Exception
3641 public function setUpPendingContribution(int $priceFieldValueID, $contriParams = []): void
{
3642 $contactID = $this->individualCreate();
3643 $contribution = $this->callAPISuccess('Order', 'create', array_merge([
3645 'contact_id' => $contactID,
3646 'receive_date' => date('Ymd'),
3647 'total_amount' => 20.00,
3648 'financial_type_id' => 1,
3649 'payment_instrument_id' => 'Credit Card',
3650 'non_deductible_amount' => 10.00,
3651 'trxn_id' => 'abcd',
3652 'invoice_id' => 'inv',
3654 'contribution_status_id' => 2,
3655 'contribution_page_id' => $this->_ids
['contribution_page'],
3660 'price_field_id' => $this->_ids
['price_field'][0],
3662 'entity_table' => 'civicrm_membership',
3665 'financial_type_id' => 1,
3666 'price_field_value_id' => $priceFieldValueID,
3670 'contact_id' => $contactID,
3671 'membership_type_id' => $this->_ids
['membership_type'],
3672 'start_date' => 'yesterday - 1 year',
3673 'end_date' => 'yesterday',
3674 'join_date' => 'yesterday - 1 year',
3680 $this->_ids
['contact'] = $contactID;
3681 $this->_ids
['contribution'] = $contribution['id'];
3682 $this->_ids
['membership'] = $this->callAPISuccessGetValue('MembershipPayment', ['return' => 'membership_id', 'contribution_id' => $contribution['id']]);
3686 * Test sending a mail via the API.
3688 * @throws \CRM_Core_Exception
3690 public function testSendMail(): void
{
3691 $mut = new CiviMailUtils($this, TRUE);
3692 $orderParams = $this->_params
;
3693 $orderParams['contribution_status_id'] = 'Pending';
3694 $orderParams['api.PaymentProcessor.pay'] = [
3695 'payment_processor_id' => $this->paymentProcessorID
,
3696 'credit_card_type' => 'Visa',
3697 'credit_card_number' => 41111111111111,
3701 $order = $this->callAPISuccess('Order', 'create', $orderParams);
3702 $this->callAPISuccess('Payment', 'create', ['total_amount' => 5, 'is_send_notification' => 0, 'order_id' => $order['id']]);
3703 $address = $this->callAPISuccess('Address', 'create', ['contribution_id' => $order['id'], 'name' => 'bob', 'contact_id' => 1, 'street_address' => 'blah']);
3704 $this->callAPISuccess('Contribution', 'create', ['id' => $order['id'], 'address_id' => $address['id']]);
3705 $this->callAPISuccess('contribution', 'sendconfirmation', [
3706 'id' => $order['id'],
3707 'receipt_from_email' => 'api@civicrm.org',
3709 $mut->checkMailLog([
3711 'Contribution Information',
3716 $this->checkCreditCardDetails($mut, $order['id']);
3718 $tplVars = CRM_Core_Smarty
::singleton()->get_template_vars();
3719 $this->assertEquals('bob', $tplVars['billingName']);
3723 * Test sending a mail via the API.
3724 * This simulates webform_civicrm using pay later contribution page
3726 * @throws \CRM_Core_Exception
3727 * @throws \CiviCRM_API3_Exception
3729 public function testSendConfirmationPayLater(): void
{
3730 $mut = new CiviMailUtils($this, TRUE);
3731 // This probably needs to call the order api in order to generate valid financial entities.
3732 $this->isValidateFinancialsOnPostAssert
= FALSE;
3733 // Create contribution page
3735 'title' => 'Webform Contributions',
3736 'financial_type_id' => 1,
3737 'contribution_type_id' => 1,
3738 'is_confirm_enabled' => 1,
3739 'is_pay_later' => 1,
3740 'pay_later_text' => 'I will send payment by cheque',
3741 'pay_later_receipt' => 'Send your cheque payable to "CiviCRM LLC" to the office',
3743 $contributionPage = $this->callAPISuccess('contribution_page', 'create', $pageParams);
3745 // Create pay later contribution
3747 'contact_id' => $this->_individualId
,
3748 'financial_type_id' => 1,
3749 'is_pay_later' => 1,
3750 'contribution_status_id' => 2,
3751 'contribution_page_id' => $contributionPage['id'],
3752 'total_amount' => '10.00',
3754 $contribution = $this->callAPISuccess('contribution', 'create', $contribParams);
3758 'contribution_id' => $contribution['id'],
3759 'entity_id' => $contribution['id'],
3760 'entity_table' => 'civicrm_contribution',
3761 'label' => 'My lineitem label',
3763 'unit_price' => '10.00',
3764 'line_total' => '10.00',
3766 $this->callAPISuccess('LineItem', 'create', $lineItemParams);
3770 civicrm_api3('contribution', 'sendconfirmation', [
3771 'id' => $contribution['id'],
3772 'receipt_from_email' => 'api@civicrm.org',
3775 catch (Exception
$e) {
3776 // Need to figure out how to stop this some other day
3777 // We don't care about the Payment Processor because this is Pay Later
3778 // The point of this test is to check we get the pay_later version of the mail
3779 if ($e->getMessage() !== "Undefined variable: CRM16923AnUnreliableMethodHasBeenUserToDeterminePaymentProcessorFromContributionPage") {
3784 // Retrieve mail & check it has the pay_later_receipt info
3785 $mut->getMostRecentEmail('raw');
3786 $mut->checkMailLog([
3787 (string) $contribParams['total_amount'],
3788 $pageParams['pay_later_receipt'],
3792 $this->checkReceiptDetails($mut, $contributionPage['id'], $contribution['id'], $pageParams);
3797 * Check credit card details in sent mail via API
3799 * @param CiviMailUtils $mut
3800 * @param int $contributionID Contribution ID
3802 * @throws \CRM_Core_Exception
3804 public function checkCreditCardDetails($mut, $contributionID) {
3805 $this->callAPISuccess('contribution', 'create', $this->_params
);
3806 $this->callAPISuccess('contribution', 'sendconfirmation', [
3807 'id' => $contributionID,
3808 'receipt_from_email' => 'api@civicrm.org',
3809 'payment_processor_id' => $this->paymentProcessorID
,
3811 $mut->checkMailLog([
3813 'Billing Name and Address',
3815 'anthony_anderson@civicrm.org',
3822 * Check receipt details in sent mail via API
3824 * @param CiviMailUtils $mut
3825 * @param int $pageID Page ID
3826 * @param int $contributionID Contribution ID
3827 * @param array $pageParams
3829 * @throws \CRM_Core_Exception
3831 public function checkReceiptDetails($mut, $pageID, $contributionID, $pageParams): void
{
3833 'receipt_from_name' => 'Page FromName',
3834 'receipt_from_email' => 'page_from@email.com',
3835 'cc_receipt' => 'page_cc@email.com',
3836 'receipt_text' => 'Page Receipt Text',
3837 'pay_later_receipt' => $pageParams['pay_later_receipt'],
3840 'receipt_from_name' => 'Custom FromName',
3841 'receipt_from_email' => 'custom_from@email.com',
3842 'cc_receipt' => 'custom_cc@email.com',
3843 'receipt_text' => 'Test Custom Receipt Text',
3844 'pay_later_receipt' => 'Mail your check to test@example.com within 3 business days.',
3846 $this->callAPISuccess('ContributionPage', 'create', array_merge([
3848 'is_email_receipt' => 1,
3851 $this->callAPISuccess('contribution', 'sendconfirmation', array_merge([
3852 'id' => $contributionID,
3853 'payment_processor_id' => $this->paymentProcessorID
,
3854 ], $customReceipt));
3856 //Verify if custom receipt details are present in email.
3857 //Page receipt details shouldn't be included.
3858 $mut->checkMailLog(array_values($customReceipt), array_values($pageReceipt));
3862 * Test sending a mail via the API.
3864 public function testSendMailEvent() {
3865 $mut = new CiviMailUtils($this, TRUE);
3866 $contribution = $this->callAPISuccess('contribution', 'create', $this->_params
);
3867 $event = $this->eventCreate([
3868 'is_email_confirm' => 1,
3869 'confirm_from_email' => 'test@civicrm.org',
3871 $this->_eventID
= $event['id'];
3872 $participantParams = [
3873 'contact_id' => $this->_individualId
,
3874 'event_id' => $this->_eventID
,
3877 // to ensure it matches later on
3878 'register_date' => '2007-07-21 00:00:00',
3879 'source' => 'Online Event Registration: API Testing',
3882 $participant = $this->callAPISuccess('participant', 'create', $participantParams);
3883 $this->callAPISuccess('participant_payment', 'create', [
3884 'participant_id' => $participant['id'],
3885 'contribution_id' => $contribution['id'],
3887 $this->callAPISuccess('contribution', 'sendconfirmation', [
3888 'id' => $contribution['id'],
3889 'receipt_from_email' => 'api@civicrm.org',
3892 $mut->checkMailLog([
3893 'Annual CiviCRM meet',
3895 'To: "Mr. Anthony Anderson II" <anthony_anderson@civicrm.org>',
3901 * This function does a GET & compares the result against the $params.
3903 * Use as a double check on Creates.
3905 * @param array $params
3907 * @param bool $delete
3909 * @throws \CRM_Core_Exception
3911 public function contributionGetnCheck(array $params, int $id, bool $delete = TRUE): void
{
3912 $contribution = $this->callAPISuccess('Contribution', 'Get', [
3917 $this->callAPISuccess('contribution', 'delete', ['id' => $id]);
3919 $this->assertAPISuccess($contribution, 0);
3920 $values = $contribution['values'][$contribution['id']];
3921 $params['receive_date'] = date('Y-m-d H:i:s', strtotime($params['receive_date']));
3922 // this is not returned in id format
3923 unset($params['payment_instrument_id']);
3924 $params['contribution_source'] = $params['source'];
3925 unset($params['source'], $params['sequential']);
3926 foreach ($params as $key => $value) {
3927 $this->assertEquals($value, $values[$key], $key . " value: $value doesn't match " . print_r($values, TRUE));
3932 * Create a pending contribution & linked pending pledge record.
3934 * @throws \CRM_Core_Exception
3936 public function createPendingPledgeContribution() {
3938 $pledgeID = $this->pledgeCreate(['contact_id' => $this->_individualId
, 'installments' => 1, 'amount' => 500]);
3939 $this->_ids
['pledge'] = $pledgeID;
3940 $contribution = $this->callAPISuccess('Contribution', 'create', array_merge($this->_params
, [
3941 'contribution_status_id' => 'Pending',
3942 'total_amount' => 500,
3944 $paymentID = $this->callAPISuccessGetValue('PledgePayment', [
3945 'options' => ['limit' => 1],
3948 $this->callAPISuccess('PledgePayment', 'create', [
3950 'contribution_id' =>
3951 $contribution['id'],
3952 'status_id' => 'Pending',
3953 'scheduled_amount' => 500,
3956 return $contribution['id'];
3960 * Create a pending contribution & linked pending participant record (along
3963 * @throws \CRM_Core_Exception
3965 public function createPendingParticipantContribution() {
3966 $this->_ids
['event']['test'] = $this->eventCreate(['is_email_confirm' => 1, 'confirm_from_email' => 'test@civicrm.org'])['id'];
3967 $participantID = $this->participantCreate(['event_id' => $this->_ids
['event']['test'], 'status_id' => 6, 'contact_id' => $this->_individualId
]);
3968 $this->_ids
['participant'] = $participantID;
3969 $params = array_merge($this->_params
, ['contact_id' => $this->_individualId
, 'contribution_status_id' => 2, 'financial_type_id' => 'Event Fee']);
3970 $contribution = $this->callAPISuccess('contribution', 'create', $params);
3971 $this->callAPISuccess('participant_payment', 'create', [
3972 'contribution_id' => $contribution['id'],
3973 'participant_id' => $participantID,
3975 $this->callAPISuccess('line_item', 'get', [
3976 'entity_id' => $contribution['id'],
3977 'entity_table' => 'civicrm_contribution',
3978 'api.line_item.create' => [
3979 'entity_id' => $participantID,
3980 'entity_table' => 'civicrm_participant',
3983 return $contribution['id'];
3987 * Get financial transaction amount.
3989 * @param int $contId
3991 * @return null|string
3993 public function _getFinancialTrxnAmount($contId) {
3995 SUM( ft.total_amount ) AS total
3996 FROM civicrm_financial_trxn AS ft
3997 LEFT JOIN civicrm_entity_financial_trxn AS ceft ON ft.id = ceft.financial_trxn_id
3998 WHERE ceft.entity_table = 'civicrm_contribution'
3999 AND ceft.entity_id = {$contId}";
4001 return CRM_Core_DAO
::singleValueQuery($query);
4005 * @param int $contId
4007 * @return null|string
4009 public function _getFinancialItemAmount($contId) {
4010 $lineItem = key(CRM_Price_BAO_LineItem
::getLineItems($contId, 'contribution'));
4013 FROM civicrm_financial_item
4014 WHERE entity_table = 'civicrm_line_item'
4015 AND entity_id = {$lineItem}";
4016 return CRM_Core_DAO
::singleValueQuery($query);
4020 * @param int $contId
4023 public function _checkFinancialItem($contId, $context) {
4024 if ($context !== 'paylater') {
4026 'entity_id' => $contId,
4027 'entity_table' => 'civicrm_contribution',
4029 $trxn = current(CRM_Financial_BAO_FinancialItem
::retrieveEntityFinancialTrxn($params, TRUE));
4031 'financial_trxn_id' => $trxn['financial_trxn_id'],
4032 'entity_table' => 'civicrm_financial_item',
4034 $entityTrxn = current(CRM_Financial_BAO_FinancialItem
::retrieveEntityFinancialTrxn($entityParams));
4036 'id' => $entityTrxn['entity_id'],
4039 if ($context === 'paylater') {
4040 $lineItems = CRM_Price_BAO_LineItem
::getLineItems($contId, 'contribution');
4041 foreach ($lineItems as $key => $item) {
4043 'entity_id' => $key,
4044 'entity_table' => 'civicrm_line_item',
4046 $compareParams = ['status_id' => 1];
4047 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialItem', $params, $compareParams);
4050 elseif ($context === 'refund') {
4053 'financial_account_id' => 1,
4057 elseif ($context === 'cancelPending') {
4060 'financial_account_id' => 1,
4064 elseif ($context === 'changeFinancial') {
4065 $lineKey = key(CRM_Price_BAO_LineItem
::getLineItems($contId, 'contribution'));
4067 'entity_id' => $lineKey,
4071 'financial_account_id' => 1,
4073 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialItem', $params, $compareParams);
4075 'financial_account_id' => 3,
4076 'entity_id' => $lineKey,
4082 if ($context !== 'paylater') {
4083 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialItem', $params, $compareParams);
4088 * Check correct financial transaction entries were created for the change in payment instrument.
4090 * @param int $contributionID
4091 * @param int $originalInstrumentID
4092 * @param int $newInstrumentID
4093 * @param int $amount
4095 public function checkFinancialTrxnPaymentInstrumentChange($contributionID, $originalInstrumentID, $newInstrumentID, $amount = 100) {
4097 $entityFinancialTrxns = $this->getFinancialTransactionsForContribution($contributionID);
4099 $originalTrxnParams = [
4100 'to_financial_account_id' => CRM_Financial_BAO_FinancialTypeAccount
::getInstrumentFinancialAccount($originalInstrumentID),
4101 'payment_instrument_id' => $originalInstrumentID,
4102 'amount' => $amount,
4106 $reversalTrxnParams = [
4107 'to_financial_account_id' => CRM_Financial_BAO_FinancialTypeAccount
::getInstrumentFinancialAccount($originalInstrumentID),
4108 'payment_instrument_id' => $originalInstrumentID,
4109 'amount' => -$amount,
4114 'to_financial_account_id' => CRM_Financial_BAO_FinancialTypeAccount
::getInstrumentFinancialAccount($newInstrumentID),
4115 'payment_instrument_id' => $newInstrumentID,
4116 'amount' => $amount,
4120 foreach ([$originalTrxnParams, $reversalTrxnParams, $newTrxnParams] as $index => $transaction) {
4121 $entityFinancialTrxn = $entityFinancialTrxns[$index];
4122 $this->assertEquals($entityFinancialTrxn['amount'], $transaction['amount']);
4124 $financialTrxn = $this->callAPISuccessGetSingle('FinancialTrxn', [
4125 'id' => $entityFinancialTrxn['financial_trxn_id'],
4127 $this->assertEquals($transaction['status_id'], $financialTrxn['status_id']);
4128 $this->assertEquals($transaction['amount'], $financialTrxn['total_amount']);
4129 $this->assertEquals($transaction['amount'], $financialTrxn['net_amount']);
4130 $this->assertEquals(0, $financialTrxn['fee_amount']);
4131 $this->assertEquals($transaction['payment_instrument_id'], $financialTrxn['payment_instrument_id']);
4132 $this->assertEquals($transaction['to_financial_account_id'], $financialTrxn['to_financial_account_id']);
4135 $this->assertEquals(1, $financialTrxn['is_payment']);
4136 $this->assertEquals('USD', $financialTrxn['currency']);
4137 $this->assertEquals(date('Y-m-d'), date('Y-m-d', strtotime($financialTrxn['trxn_date'])));
4142 * Check financial transaction.
4144 * @todo break this down into sensible functions - most calls to it only use a few lines out of the big if.
4146 * @param array $contribution
4147 * @param string $context
4148 * @param int $instrumentId
4149 * @param array $extraParams
4151 public function _checkFinancialTrxn($contribution, $context, $instrumentId = NULL, $extraParams = []) {
4152 $financialTrxns = $this->getFinancialTransactionsForContribution($contribution['id']);
4153 $trxn = array_pop($financialTrxns);
4156 'id' => $trxn['financial_trxn_id'],
4158 if ($context === 'payLater') {
4161 'from_financial_account_id' => CRM_Contribute_PseudoConstant
::getRelationalFinancialAccount($contribution['financial_type_id'], 'Accounts Receivable Account is'),
4164 elseif ($context === 'refund') {
4166 'to_financial_account_id' => 6,
4167 'total_amount' => -100,
4169 'trxn_date' => '2015-01-01 09:00:00',
4170 'trxn_id' => 'the refund',
4173 elseif ($context === 'cancelPending') {
4175 'to_financial_account_id' => 7,
4176 'total_amount' => -100,
4180 elseif ($context === 'changeFinancial' ||
$context === 'paymentInstrument') {
4181 // @todo checkFinancialTrxnPaymentInstrumentChange instead for paymentInstrument.
4182 // It does the same thing with greater readability.
4183 // @todo remove handling for
4186 'entity_id' => $contribution['id'],
4187 'entity_table' => 'civicrm_contribution',
4190 $trxn = current(CRM_Financial_BAO_FinancialItem
::retrieveEntityFinancialTrxn($entityParams));
4192 'id' => $trxn['financial_trxn_id'],
4194 if (empty($extraParams)) {
4196 'total_amount' => -100,
4200 elseif ($context !== 'changeFinancial') {
4202 'total_amount' => 100,
4206 if ($context === 'paymentInstrument') {
4207 $compareParams['to_financial_account_id'] = CRM_Financial_BAO_FinancialTypeAccount
::getInstrumentFinancialAccount($instrumentId);
4208 $compareParams['payment_instrument_id'] = $instrumentId;
4211 $compareParams['to_financial_account_id'] = 12;
4213 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialTrxn', $trxnParams1, array_merge($compareParams, $extraParams));
4214 $compareParams['total_amount'] = 100;
4215 // Reverse the extra params now that we will be checking the new positive transaction.
4216 if ($context === 'changeFinancial' && !empty($extraParams)) {
4217 foreach ($extraParams as $param => $value) {
4218 $extraParams[$param] = 0 - $value;
4223 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialTrxn', $params, array_merge($compareParams, $extraParams));
4229 public function _addPaymentInstrument() {
4230 $gId = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_OptionGroup', 'payment_instrument', 'id', 'name');
4232 'option_group_id' => $gId,
4233 'label' => 'Test Card',
4234 'name' => 'Test Card',
4239 $optionValue = $this->callAPISuccess('option_value', 'create', $optionParams);
4240 $relationTypeId = key(CRM_Core_PseudoConstant
::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Asset Account is' "));
4241 $financialParams = [
4242 'entity_table' => 'civicrm_option_value',
4243 'entity_id' => $optionValue['id'],
4244 'account_relationship' => $relationTypeId,
4245 'financial_account_id' => 7,
4247 CRM_Financial_BAO_FinancialTypeAccount
::add($financialParams);
4248 $this->assertNotEmpty($optionValue['values'][$optionValue['id']]['value']);
4249 return $optionValue['values'][$optionValue['id']]['value'];
4252 public function _deletedAddedPaymentInstrument() {
4253 $result = $this->callAPISuccess('OptionValue', 'get', [
4254 'option_group_id' => 'payment_instrument',
4255 'name' => 'Test Card',
4259 if ($id = CRM_Utils_Array
::value('id', $result)) {
4260 $this->callAPISuccess('OptionValue', 'delete', ['id' => $id]);
4265 * Set up the basic recurring contribution for tests.
4267 * @param array $generalParams
4268 * Parameters that can be merged into the recurring AND the contribution.
4270 * @param array $recurParams
4271 * Parameters to merge into the recur only.
4274 * @throws \CRM_Core_Exception
4276 protected function setUpRecurringContribution($generalParams = [], $recurParams = []) {
4277 $contributionRecur = $this->callAPISuccess('contribution_recur', 'create', array_merge([
4278 'contact_id' => $this->_individualId
,
4279 'installments' => '12',
4280 'frequency_interval' => '1',
4282 'contribution_status_id' => 1,
4283 'start_date' => '2012-01-01 00:00:00',
4284 'currency' => 'USD',
4285 'frequency_unit' => 'month',
4286 'payment_processor_id' => $this->paymentProcessorID
,
4287 ], $generalParams, $recurParams));
4288 $contributionParams = array_merge(
4291 'contribution_recur_id' => $contributionRecur['id'],
4292 'contribution_status_id' => 'Pending',
4294 $contributionParams['api.Payment.create'] = ['total_amount' => $contributionParams['total_amount']];
4295 $originalContribution = $this->callAPISuccess('Order', 'create', $contributionParams);
4296 return $originalContribution;
4300 * Set up a basic auto-renew membership for tests.
4302 * @param array $generalParams
4303 * Parameters that can be merged into the recurring AND the contribution.
4305 * @param array $recurParams
4306 * Parameters to merge into the recur only.
4309 * @throws \CRM_Core_Exception
4311 protected function setUpAutoRenewMembership($generalParams = [], $recurParams = []) {
4312 $newContact = $this->callAPISuccess('Contact', 'create', [
4313 'contact_type' => 'Individual',
4314 'sort_name' => 'McTesterson, Testy',
4315 'display_name' => 'Testy McTesterson',
4316 'preferred_language' => 'en_US',
4317 'preferred_mail_format' => 'Both',
4318 'first_name' => 'Testy',
4319 'last_name' => 'McTesterson',
4320 'contact_is_deleted' => '0',
4322 'email' => 'tmctesterson@example.com',
4325 $membershipType = $this->callAPISuccess('MembershipType', 'create', [
4326 'domain_id' => 'Default Domain Name',
4327 'member_of_contact_id' => 1,
4328 'financial_type_id' => 'Member Dues',
4329 'duration_unit' => 'month',
4330 'duration_interval' => 1,
4331 'period_type' => 'rolling',
4332 'name' => 'Standard Member',
4333 'minimum_fee' => 100,
4335 $contributionRecur = $this->callAPISuccess('contribution_recur', 'create', array_merge([
4336 'contact_id' => $newContact['id'],
4337 'installments' => '12',
4338 'frequency_interval' => '1',
4340 'contribution_status_id' => 1,
4341 'start_date' => '2012-01-01 00:00:00',
4342 'currency' => 'USD',
4343 'frequency_unit' => 'month',
4344 'payment_processor_id' => $this->paymentProcessorID
,
4345 ], $generalParams, $recurParams));
4347 $this->callAPISuccess('membership', 'create', [
4348 'contact_id' => $newContact['id'],
4349 'contribution_recur_id' => $contributionRecur['id'],
4350 'financial_type_id' => 'Member Dues',
4351 'membership_type_id' => $membershipType['id'],
4353 'skipLineItem' => TRUE,
4356 CRM_Price_BAO_LineItem
::getLineItemArray($this->_params
, NULL, 'membership', $membershipType['id']);
4357 $originalContribution = $this->callAPISuccess('contribution', 'create', array_merge(
4360 'contact_id' => $newContact['id'],
4361 'contribution_recur_id' => $contributionRecur['id'],
4362 'financial_type_id' => 'Member Dues',
4363 'contribution_status_id' => 1,
4364 'invoice_id' => 2345,
4367 $lineItem = $this->callAPISuccess('LineItem', 'getsingle', []);
4368 $this->assertEquals('civicrm_membership', $lineItem['entity_table']);
4369 $membership = $this->callAPISuccess('Membership', 'getsingle', ['id' => $lineItem['entity_id']]);
4370 $this->callAPISuccess('LineItem', 'getsingle', []);
4371 $this->callAPISuccessGetCount('MembershipPayment', ['membership_id' => $membership['id']], 1);
4373 return [$originalContribution, $membership];
4377 * Set up a repeat transaction.
4379 * @param array $recurParams
4380 * @param mixed $flag
4381 * @param array $contributionParams
4384 * @throws \CRM_Core_Exception
4386 protected function setUpRepeatTransaction($recurParams, $flag, $contributionParams = []) {
4387 $paymentProcessorID = $this->paymentProcessorCreate();
4388 $contributionRecur = $this->callAPISuccess('contribution_recur', 'create', array_merge([
4389 'contact_id' => $this->_individualId
,
4390 'installments' => '12',
4391 'frequency_interval' => '1',
4393 'contribution_status_id' => 1,
4394 'start_date' => '2012-01-01 00:00:00',
4395 'currency' => 'USD',
4396 'frequency_unit' => 'month',
4397 'payment_processor_id' => $paymentProcessorID,
4400 $originalContribution = '';
4401 if ($flag === 'multiple') {
4402 // CRM-19309 create a contribution + also add in line_items (plural):
4403 $params = array_merge($this->_params
, $contributionParams);
4404 $originalContribution = $this->callAPISuccess('contribution', 'create', array_merge(
4407 'contribution_recur_id' => $contributionRecur['id'],
4408 'skipLineItem' => 1,
4409 'api.line_item.create' => [
4411 'price_field_id' => 1,
4413 'line_total' => '20',
4414 'unit_price' => '10',
4415 'financial_type_id' => 1,
4418 'price_field_id' => 1,
4420 'line_total' => '80',
4421 'unit_price' => '80',
4422 'financial_type_id' => 2,
4429 elseif ($flag === 'single') {
4430 $params = array_merge($this->_params
, ['contribution_recur_id' => $contributionRecur['id']]);
4431 $params = array_merge($params, $contributionParams);
4432 $originalContribution = $this->callAPISuccess('contribution', 'create', $params);
4434 $originalContribution['contribution_recur_id'] = $contributionRecur['id'];
4435 $originalContribution['payment_processor_id'] = $paymentProcessorID;
4436 return $originalContribution;
4440 * Common set up routine.
4443 * @throws \CRM_Core_Exception
4445 protected function setUpForCompleteTransaction(): array {
4446 $this->mut
= new CiviMailUtils($this, TRUE);
4447 $this->createLoggedInUser();
4448 $params = array_merge($this->_params
, ['contribution_status_id' => 2, 'receipt_date' => 'now']);
4449 return $this->callAPISuccess('Contribution', 'create', $params);
4453 * Test repeat contribution uses the Payment Processor' payment_instrument setting.
4455 * @throws \CRM_Core_Exception
4457 public function testRepeatTransactionWithNonCreditCardDefault() {
4458 $contributionRecur = $this->callAPISuccess('ContributionRecur', 'create', [
4459 'contact_id' => $this->_individualId
,
4460 'installments' => '12',
4461 'frequency_interval' => '1',
4463 'contribution_status_id' => 1,
4464 'start_date' => '2012-01-01 00:00:00',
4465 'currency' => 'USD',
4466 'frequency_unit' => 'month',
4467 'payment_processor_id' => $this->paymentProcessorID
,
4469 $contribution1 = $this->callAPISuccess('contribution', 'create', array_merge(
4471 ['contribution_recur_id' => $contributionRecur['id'], 'payment_instrument_id' => 2])
4473 $contribution2 = $this->callAPISuccess('contribution', 'repeattransaction', [
4474 'contribution_status_id' => 'Completed',
4475 'trxn_id' => 'blah',
4476 'original_contribution_id' => $contribution1,
4478 $this->assertEquals('Debit Card', CRM_Contribute_PseudoConstant
::getLabel('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', $contribution2['values'][$contribution2['id']]['payment_instrument_id']));
4482 * CRM-20008 Tests repeattransaction creates pending membership.
4484 * @throws \CRM_Core_Exception
4486 public function testRepeatTransactionMembershipCreatePendingContribution(): void
{
4487 [$originalContribution, $membership] = $this->setUpAutoRenewMembership();
4488 $this->callAPISuccess('membership', 'create', [
4489 'id' => $membership['id'],
4490 'end_date' => 'yesterday',
4491 'status_id' => 'Expired',
4493 $repeatedContribution = $this->callAPISuccess('contribution', 'repeattransaction', [
4494 'contribution_recur_id' => $originalContribution['values'][1]['contribution_recur_id'],
4495 'contribution_status_id' => 'Pending',
4498 $membershipStatusId = $this->callAPISuccess('membership', 'getvalue', [
4499 'id' => $membership['id'],
4500 'return' => 'status_id',
4503 // Let's see if the membership payments got created while we're at it.
4504 $membershipPayments = $this->callAPISuccess('MembershipPayment', 'get', [
4505 'membership_id' => $membership['id'],
4507 $this->assertEquals(2, $membershipPayments['count']);
4509 $this->assertEquals('Expired', CRM_Core_PseudoConstant
::getLabel('CRM_Member_BAO_Membership', 'status_id', $membershipStatusId));
4510 $this->callAPISuccess('Contribution', 'completetransaction', ['id' => $repeatedContribution['id']]);
4511 $membership = $this->callAPISuccessGetSingle('membership', [
4512 'id' => $membership['id'],
4513 'return' => 'status_id, end_date',
4515 $this->assertEquals('New', CRM_Core_PseudoConstant
::getName('CRM_Member_BAO_Membership', 'status_id', $membership['status_id']));
4516 $this->assertEquals(date('Y-m-d', strtotime('yesterday + 1 month')), $membership['end_date']);
4520 * Test sending a mail via the API.
4522 * @throws \CRM_Core_Exception
4524 public function testSendMailWithAPISetFromDetails() {
4525 $mut = new CiviMailUtils($this, TRUE);
4526 $contribution = $this->callAPISuccess('contribution', 'create', $this->_params
);
4527 $this->callAPISuccess('contribution', 'sendconfirmation', [
4528 'id' => $contribution['id'],
4529 'receipt_from_email' => 'api@civicrm.org',
4530 'receipt_from_name' => 'CiviCRM LLC',
4532 $mut->checkMailLog([
4533 'From: CiviCRM LLC <api@civicrm.org>',
4534 'Contribution Information',
4542 * Test sending a mail via the API.
4544 public function testSendMailWithNoFromSetFallToDomain() {
4545 $this->createLoggedInUser();
4546 $mut = new CiviMailUtils($this, TRUE);
4547 $contribution = $this->callAPISuccess('contribution', 'create', $this->_params
);
4548 $this->callAPISuccess('contribution', 'sendconfirmation', [
4549 'id' => $contribution['id'],
4551 $domain = $this->callAPISuccess('domain', 'getsingle', ['id' => 1]);
4552 $mut->checkMailLog([
4553 'From: ' . $domain['from_name'] . ' <' . $domain['from_email'] . '>',
4554 'Contribution Information',
4562 * Test sending a mail via the API.
4564 * @throws \CRM_Core_Exception
4566 public function testSendMailWithRepeatTransactionAPIFalltoDomain() {
4567 $this->createLoggedInUser();
4568 $mut = new CiviMailUtils($this, TRUE);
4569 $contribution = $this->setUpRepeatTransaction([], 'single');
4570 $this->callAPISuccess('contribution', 'repeattransaction', [
4571 'contribution_status_id' => 'Completed',
4573 'original_contribution_id' => $contribution,
4575 $domain = $this->callAPISuccess('domain', 'getsingle', ['id' => 1]);
4576 $mut->checkMailLog([
4577 'From: ' . $domain['from_name'] . ' <' . $domain['from_email'] . '>',
4578 'Contribution Information',
4587 * Test sending a mail via the API.
4589 * @throws \CRM_Core_Exception
4591 public function testSendMailWithRepeatTransactionAPIFalltoContributionPage() {
4592 $mut = new CiviMailUtils($this, TRUE);
4593 $contributionPage = $this->contributionPageCreate(['receipt_from_name' => 'CiviCRM LLC', 'receipt_from_email' => 'contributionpage@civicrm.org', 'is_email_receipt' => 1]);
4594 $paymentProcessorID = $this->paymentProcessorCreate();
4595 $contributionRecur = $this->callAPISuccess('contribution_recur', 'create', [
4596 'contact_id' => $this->_individualId
,
4597 'installments' => '12',
4598 'frequency_interval' => '1',
4600 'contribution_status_id' => 1,
4601 'start_date' => '2012-01-01 00:00:00',
4602 'currency' => 'USD',
4603 'frequency_unit' => 'month',
4604 'payment_processor_id' => $paymentProcessorID,
4606 $originalContribution = $this->callAPISuccess('contribution', 'create', array_merge(
4609 'contribution_recur_id' => $contributionRecur['id'],
4610 'contribution_page_id' => $contributionPage['id'],
4613 $this->callAPISuccess('Contribution', 'repeattransaction', [
4614 'contribution_status_id' => 'Completed',
4616 'original_contribution_id' => $originalContribution,
4619 $mut->checkMailLog([
4620 'From: CiviCRM LLC <contributionpage@civicrm.org>',
4621 'Contribution Information',
4629 * Test sending a mail via the API.
4631 * @throws \CRM_Core_Exception
4633 public function testSendMailWithRepeatTransactionAPIFalltoSystemFromNoDefaultFrom(): void
{
4634 $mut = new CiviMailUtils($this, TRUE);
4635 $originalContribution = $this->setUpRepeatTransaction([], 'single');
4636 $fromEmail = $this->callAPISuccess('optionValue', 'get', ['is_default' => 1, 'option_group_id' => 'from_email_address', 'sequential' => 1]);
4637 foreach ($fromEmail['values'] as $from) {
4638 $this->callAPISuccess('optionValue', 'create', ['is_default' => 0, 'id' => $from['id']]);
4640 $domain = $this->callAPISuccess('domain', 'getsingle', ['id' => CRM_Core_Config
::domainID()]);
4641 $this->callAPISuccess('contribution', 'repeattransaction', [
4642 'contribution_status_id' => 'Completed',
4644 'original_contribution_id' => $originalContribution,
4646 $mut->checkMailLog([
4647 'From: ' . $domain['name'] . ' <' . $domain['domain_email'] . '>',
4648 'Contribution Information',
4656 * Create a Contribution Page with is_email_receipt = TRUE.
4658 * @param array $params
4659 * Params to overwrite with.
4663 protected function createReceiptableContributionPage($params = []) {
4664 $contributionPage = $this->callAPISuccess('ContributionPage', 'create', array_merge([
4665 'receipt_from_name' => 'Mickey Mouse',
4666 'receipt_from_email' => 'mickey@mouse.com',
4667 'title' => "Test Contribution Page",
4668 'financial_type_id' => 1,
4669 'currency' => 'CAD',
4670 'is_monetary' => TRUE,
4671 'is_email_receipt' => TRUE,
4673 return $contributionPage;
4677 * function to test card_type and pan truncation.
4679 * @throws \CRM_Core_Exception
4681 public function testCardTypeAndPanTruncation() {
4682 $creditCardTypeIDs = array_flip(CRM_Financial_DAO_FinancialTrxn
::buildOptions('card_type_id'));
4683 $contactId = $this->individualCreate();
4685 'contact_id' => $contactId,
4686 'receive_date' => '2016-01-20',
4687 'total_amount' => 100,
4688 'financial_type_id' => 1,
4689 'payment_instrument' => 'Credit Card',
4690 'card_type_id' => $creditCardTypeIDs['Visa'],
4691 'pan_truncation' => 4567,
4693 $contribution = $this->callAPISuccess('contribution', 'create', $params);
4694 $lastFinancialTrxnId = CRM_Core_BAO_FinancialTrxn
::getFinancialTrxnId($contribution['id'], 'DESC');
4695 $financialTrxn = $this->callAPISuccessGetSingle(
4698 'id' => $lastFinancialTrxnId['financialTrxnId'],
4699 'return' => ['card_type_id', 'pan_truncation'],
4702 $this->assertEquals(CRM_Utils_Array
::value('card_type_id', $financialTrxn), $creditCardTypeIDs['Visa']);
4703 $this->assertEquals(CRM_Utils_Array
::value('pan_truncation', $financialTrxn), 4567);
4705 'id' => $contribution['id'],
4706 'pan_truncation' => 2345,
4707 'card_type_id' => $creditCardTypeIDs['Amex'],
4709 $this->callAPISuccess('contribution', 'create', $params);
4710 $financialTrxn = $this->callAPISuccessGetSingle(
4713 'id' => $lastFinancialTrxnId['financialTrxnId'],
4714 'return' => ['card_type_id', 'pan_truncation'],
4717 $this->assertEquals(CRM_Utils_Array
::value('card_type_id', $financialTrxn), $creditCardTypeIDs['Amex']);
4718 $this->assertEquals(CRM_Utils_Array
::value('pan_truncation', $financialTrxn), 2345);
4722 * Test repeat contribution uses non default currency
4724 * @see https://issues.civicrm.org/jira/projects/CRM/issues/CRM-20678
4725 * @throws \CRM_Core_Exception
4727 public function testRepeatTransactionWithDifferenceCurrency() {
4728 $originalContribution = $this->setUpRepeatTransaction(['currency' => 'AUD'], 'single', ['currency' => 'AUD']);
4729 $contribution = $this->callAPISuccess('Contribution', 'repeattransaction', [
4730 'original_contribution_id' => $originalContribution['id'],
4731 'contribution_status_id' => 'Completed',
4734 $this->assertEquals('AUD', $contribution['values'][$contribution['id']]['currency']);
4738 * Get the financial items for the contribution.
4740 * @param int $contributionID
4743 * Array of associated financial items.
4745 protected function getFinancialTransactionsForContribution($contributionID) {
4747 'entity_id' => $contributionID,
4748 'entity_table' => 'civicrm_contribution',
4750 // @todo the following function has naming errors & has a weird signature & appears to
4751 // only be called from test classes. Move into test suite & maybe just use api
4752 // from this function.
4753 return array_merge(CRM_Financial_BAO_FinancialItem
::retrieveEntityFinancialTrxn($trxnParams));
4757 * Test getunique api call for Contribution entity
4759 public function testContributionGetUnique() {
4760 $result = $this->callAPIAndDocument($this->entity
, 'getunique', [], __FUNCTION__
, __FILE__
);
4761 $this->assertEquals(2, $result['count']);
4762 $this->assertEquals(['trxn_id'], $result['values']['UI_contrib_trxn_id']);
4763 $this->assertEquals(['invoice_id'], $result['values']['UI_contrib_invoice_id']);
4767 * Test Repeat Transaction Contribution with Tax amount.
4768 * https://lab.civicrm.org/dev/core/issues/806
4770 * @throws \CRM_Core_Exception
4772 public function testRepeatContributionWithTaxAmount(): void
{
4773 $this->enableTaxAndInvoicing();
4774 $financialType = $this->callAPISuccess('financial_type', 'create', [
4775 'name' => 'Test taxable financial Type',
4779 $this->addTaxAccountToFinancialType($financialType['id']);
4780 $contribution = $this->setUpRepeatTransaction(
4784 'financial_type_id' => $financialType['id'],
4787 $this->callAPISuccess('contribution', 'repeattransaction', [
4788 'original_contribution_id' => $contribution['id'],
4789 'contribution_status_id' => 'Completed',
4790 'trxn_id' => 'test',
4792 $payments = $this->callAPISuccess('Contribution', 'get', ['sequential' => 1])['values'];
4793 //Assert if first payment and repeated payment has the same contribution amount.
4794 $this->assertEquals($payments[0]['total_amount'], $payments[1]['total_amount']);
4795 $this->callAPISuccessGetCount('Contribution', [], 2);
4797 //Assert line item records.
4798 $lineItems = $this->callAPISuccess('LineItem', 'get', ['sequential' => 1])['values'];
4799 foreach ($lineItems as $lineItem) {
4800 $this->assertEquals($lineItem['unit_price'], $this->_params
['total_amount']);
4801 $this->assertEquals($lineItem['line_total'], $this->_params
['total_amount']);
4803 $this->callAPISuccessGetCount('Contribution', [], 2);
4806 public function testGetCurrencyOptions() {
4807 $result = $this->callAPISuccess('Contribution', 'getoptions', [
4808 'field' => 'currency',
4810 $this->assertEquals('US Dollar', $result['values']['USD']);
4811 $this->assertNotContains('$', $result['values']);
4812 $result = $this->callAPISuccess('Contribution', 'getoptions', [
4813 'field' => 'currency',
4814 'context' => "abbreviate",
4816 $this->assertEquals('$', $result['values']['USD']);
4817 $this->assertNotContains('US Dollar', $result['values']);
4821 * @throws \API_Exception
4822 * @throws \CRM_Core_Exception
4823 * @throws \Civi\API\Exception\UnauthorizedException
4825 public function testSetCustomDataInCreateAndHook() {
4826 $this->createCustomGroupWithFieldOfType([], 'int');
4827 $this->ids
['CustomField']['text'] = (int) $this->createTextCustomField(['custom_group_id' => $this->ids
['CustomGroup']['Custom Group']])['id'];
4828 $this->hookClass
->setHook('civicrm_post', [
4830 'civicrmPostContributionCustom',
4832 $params = $this->_params
;
4833 $params['custom_' . $this->ids
['CustomField']['text']] = 'Some Text';
4834 $contribution = $this->callAPISuccess('Contribution', 'create', $params);
4835 $getContribution = $this->callAPISuccess('Contribution', 'get', [
4836 'id' => $contribution['id'],
4837 'return' => ['id', 'custom_' . $this->ids
['CustomField']['text'], 'custom_' . $this->ids
['CustomField']['int']],
4839 $this->assertEquals(5, $getContribution['values'][$contribution['id']][$this->getCustomFieldName('int')]);
4840 $this->assertEquals('Some Text', $getContribution['values'][$contribution['id']]['custom_' . $this->ids
['CustomField']['text']]);
4841 $this->callAPISuccess('CustomField', 'delete', ['id' => $this->ids
['CustomField']['text']]);
4842 $this->callAPISuccess('CustomField', 'delete', ['id' => $this->ids
['CustomField']['int']]);
4843 $this->callAPISuccess('CustomGroup', 'delete', ['id' => $this->ids
['CustomGroup']['Custom Group']]);
4847 * Implement post hook.
4850 * @param string $objectName
4851 * @param int|null $objectId
4853 * @throws \CRM_Core_Exception
4855 public function civicrmPostContributionCustom(string $op, string $objectName, ?
int $objectId): void
{
4856 if ($objectName === 'Contribution' && $op === 'create') {
4857 $this->callAPISuccess('Contribution', 'create', [
4859 'custom_' . $this->ids
['CustomField']['int'] => 5,
4865 * Test that passing in label for an option value linked to a custom field
4868 * @see dev/core#1816
4870 * @throws \API_Exception
4871 * @throws \CRM_Core_Exception
4873 public function testCustomValueOptionLabelTest(): void
{
4874 $this->createCustomGroupWithFieldOfType([], 'radio');
4875 $params = $this->_params
;
4876 $params['custom_' . $this->ids
['CustomField']['radio']] = 'Red Testing';
4877 $this->callAPISuccess('Contribution', 'Create', $params);
4881 * Test repeatTransaction with installments and next_sched_contribution_date
4883 * @dataProvider getRepeatTransactionNextSchedData
4885 * @param array $dataSet
4887 * @throws \CRM_Core_Exception
4889 public function testRepeatTransactionUpdateNextSchedContributionDate(array $dataSet): void
{
4890 $paymentProcessorID = $this->paymentProcessorCreate();
4891 // Create the contribution before the recur so it doesn't trigger the update of next_sched_contribution_date
4892 $contribution = $this->callAPISuccess('contribution', 'create', array_merge(
4895 'contribution_status_id' => 'Completed',
4896 'receive_date' => $dataSet['repeat'][0]['receive_date'],
4899 $contributionRecur = $this->callAPISuccess('contribution_recur', 'create', array_merge([
4900 'contact_id' => $this->_individualId
,
4901 'frequency_interval' => '1',
4903 'contribution_status_id' => 'Pending',
4904 'start_date' => '2012-01-01 00:00:00',
4905 'currency' => 'USD',
4906 'frequency_unit' => 'month',
4907 'payment_processor_id' => $paymentProcessorID,
4908 ], $dataSet['recur']));
4909 // Link the existing contribution to the recur *after* creating the recur.
4910 // If we just created the contribution now the next_sched_contribution_date would be automatically set
4911 // and we want to test the case when it is empty.
4912 $this->callAPISuccess('contribution', 'create', [
4913 'id' => $contribution['id'],
4914 'contribution_recur_id' => $contributionRecur['id'],
4917 $contributionRecur = $this->callAPISuccessGetSingle('ContributionRecur', [
4918 'id' => $contributionRecur['id'],
4919 'return' => ['next_sched_contribution_date', 'contribution_status_id'],
4921 // Check that next_sched_contribution_date is empty
4922 $this->assertEquals('', $contributionRecur['next_sched_contribution_date'] ??
'');
4924 $this->callAPISuccess('Contribution', 'repeattransaction', [
4925 'contribution_status_id' => 'Completed',
4926 'contribution_recur_id' => $contributionRecur['id'],
4927 'receive_date' => $dataSet['repeat'][0]['receive_date'],
4929 $contributionRecur = $this->callAPISuccessGetSingle('ContributionRecur', [
4930 'id' => $contributionRecur['id'],
4931 'return' => ['next_sched_contribution_date', 'contribution_status_id'],
4933 // Check that recur has status "In Progress"
4934 $this->assertEquals(
4935 (string) CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_ContributionRecur', 'contribution_status_id', $dataSet['repeat'][0]['expectedRecurStatus']),
4936 $contributionRecur['contribution_status_id']
4938 // Check that next_sched_contribution_date has been set to 1 period after the contribution receive date (ie. 1 month)
4939 $this->assertEquals($dataSet['repeat'][0]['expectedNextSched'], $contributionRecur['next_sched_contribution_date']);
4941 // Now call Contribution.repeattransaction again and check that the next_sched_contribution_date has moved forward by 1 period again
4942 $this->callAPISuccess('Contribution', 'repeattransaction', [
4943 'contribution_status_id' => 'Completed',
4944 'contribution_recur_id' => $contributionRecur['id'],
4945 'receive_date' => $dataSet['repeat'][1]['receive_date'],
4947 $contributionRecur = $this->callAPISuccessGetSingle('ContributionRecur', [
4948 'id' => $contributionRecur['id'],
4949 'return' => ['next_sched_contribution_date', 'contribution_status_id'],
4951 // Check that recur has status "In Progress" or "Completed" depending on whether number of installments has been reached
4952 $this->assertEquals(
4953 (string) CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_ContributionRecur', 'contribution_status_id', $dataSet['repeat'][1]['expectedRecurStatus']),
4954 $contributionRecur['contribution_status_id']
4956 // Check that next_sched_contribution_date has been set to 1 period after the contribution receive date (ie. 1 month)
4957 $this->assertEquals($dataSet['repeat'][1]['expectedNextSched'], $contributionRecur['next_sched_contribution_date'] ??
'');
4961 * Get dates for testing.
4965 public function getRepeatTransactionNextSchedData(): array {
4966 // Both these tests handle/test the case that next_sched_contribution_date is empty when Contribution.repeattransaction
4967 // is called for the first time. Historically setting it was inconsistent but on new updates it should always be set.
4969 * This tests that calling Contribution.repeattransaction with installments does the following:
4970 * - For the first call to repeattransaction the recur status is In Progress and next_sched_contribution_date is updated
4971 * to match next expected receive_date.
4972 * - Once the 3rd contribution is created contributionRecur status = completed and next_sched_contribution_date = ''.
4974 $result['receive_date_includes_time_with_installments']['2012-01-01-1-month'] = [
4976 'start_date' => '2012-01-01',
4977 'frequency_interval' => 1,
4978 'installments' => '3',
4979 'frequency_unit' => 'month',
4983 'receive_date' => '2012-02-29 16:00:00',
4984 'expectedNextSched' => '2012-03-29 00:00:00',
4985 'expectedRecurStatus' => 'In Progress',
4988 'receive_date' => '2012-03-29 16:00:00',
4989 'expectedNextSched' => '',
4990 'expectedRecurStatus' => 'Completed',
4995 * This tests that calling Contribution.repeattransaction with no installments does the following:
4996 * - For the each call to repeattransaction the recur status is In Progress and next_sched_contribution_date is updated
4997 * to match next expected receive_date.
4999 $result['receive_date_includes_time_no_installments']['2012-01-01-1-month'] = [
5001 'start_date' => '2012-01-01',
5002 'frequency_interval' => 1,
5003 'frequency_unit' => 'month',
5007 'receive_date' => '2012-02-29 16:00:00',
5008 'expectedNextSched' => '2012-03-29 00:00:00',
5009 'expectedRecurStatus' => 'In Progress',
5012 'receive_date' => '2012-03-29 16:00:00',
5013 'expectedNextSched' => '2012-04-29 00:00:00',
5014 'expectedRecurStatus' => 'In Progress',
5022 * Make sure that recording a payment doesn't alter the receive_date of a
5023 * pending contribution.
5025 public function testPaymentDontChangeReceiveDate(): void
{
5027 'contact_id' => $this->_individualId
,
5028 'total_amount' => 100,
5029 'receive_date' => '2020-02-02',
5030 'contribution_status_id' => 'Pending',
5032 $contributionID = $this->contributionCreate($params);
5034 'contribution_id' => $contributionID,
5035 'total_amount' => 100,
5036 'trxn_date' => '2020-03-04',
5038 $this->callAPISuccess('payment', 'create', $paymentParams);
5040 //check if contribution status is set to "Completed".
5041 $contribution = $this->callAPISuccess('Contribution', 'getSingle', [
5042 'id' => $contributionID,
5044 $this->assertEquals('2020-02-02 00:00:00', $contribution['receive_date']);
5048 * Make sure that recording a payment with Different Payment Instrument update main contribution record payment
5049 * instrument too. If multiple Payment Recorded, last payment record payment (when No more due) instrument set to main
5052 public function testPaymentVerifyPaymentInstrumentChange() {
5053 // Create Pending contribution with pay later mode, with payment instrument Check
5055 'contact_id' => $this->_individualId
,
5056 'total_amount' => 100,
5057 'receive_date' => '2020-02-02',
5058 'contribution_status_id' => 'Pending',
5059 'is_pay_later' => 1,
5060 'payment_instrument_id' => 'Check',
5062 $contributionID = $this->contributionCreate($params);
5064 // Record the the Payment with instrument other than Check, e.g EFT
5066 'contribution_id' => $contributionID,
5067 'total_amount' => 50,
5068 'trxn_date' => '2020-03-04',
5069 'payment_instrument_id' => 'EFT',
5071 $this->callAPISuccess('payment', 'create', $paymentParams);
5073 $contribution = $this->callAPISuccess('Contribution', 'getSingle', [
5074 'id' => $contributionID,
5076 // payment status should be 'Partially paid'
5077 $this->assertEquals('Partially paid', $contribution['contribution_status']);
5079 // Record the the Payment with instrument other than Check, e.g Cash (pay all remaining amount)
5081 'contribution_id' => $contributionID,
5082 'total_amount' => 50,
5083 'trxn_date' => '2020-03-04',
5084 'payment_instrument_id' => 'Cash',
5086 $this->callAPISuccess('payment', 'create', $paymentParams);
5088 //check if contribution Payment Instrument (Payment Method) is is set to "Cash".
5089 $contribution = $this->callAPISuccess('Contribution', 'getSingle', [
5090 'id' => $contributionID,
5092 $this->assertEquals('Cash', $contribution['payment_instrument']);
5093 $this->assertEquals('Completed', $contribution['contribution_status']);
5097 * Test the "clean money" functionality.
5099 public function testCleanMoney() {
5101 'contact_id' => $this->_individualId
,
5102 'financial_type_id' => 1,
5103 'total_amount' => '$100',
5104 'fee_amount' => '$20',
5105 'net_amount' => '$80',
5106 'non_deductible_amount' => '$80',
5109 $id = $this->callAPISuccess('Contribution', 'create', $params)['id'];
5110 // Reading the return values of the API isn't reliable here; get the data from the db.
5111 $contribution = $this->callAPISuccess('Contribution', 'getsingle', ['id' => $id]);
5112 $this->assertEquals('100.00', $contribution['total_amount']);
5113 $this->assertEquals('20.00', $contribution['fee_amount']);
5114 $this->assertEquals('80.00', $contribution['net_amount']);
5115 $this->assertEquals('80.00', $contribution['non_deductible_amount']);
5119 * Create a price set with a quick config price set.
5121 * The params to use this look like
5123 * ['price_' . $this->ids['PriceField']['basic'] => $this->ids['PriceFieldValue']['basic']]
5125 * @param array $contributionPageParams
5129 * @throws \API_Exception
5130 * @throws \CRM_Core_Exception
5131 * @throws \Civi\API\Exception\UnauthorizedException
5133 private function createQuickConfigContributionPage(array $contributionPageParams = []): int {
5134 $contributionPageID = $this->callAPISuccess('ContributionPage', 'create', array_merge([
5135 'receipt_from_name' => 'Mickey Mouse',
5136 'receipt_from_email' => 'mickey@mouse.com',
5137 'title' => 'Test Contribution Page',
5138 'financial_type_id' => 'Member Dues',
5139 'currency' => 'CAD',
5140 'is_pay_later' => 1,
5141 'is_quick_config' => TRUE,
5142 'pay_later_text' => 'I will send payment by check',
5143 'pay_later_receipt' => 'This is a pay later receipt',
5144 'is_allow_other_amount' => 1,
5145 'min_amount' => 10.00,
5146 'max_amount' => 10000.00,
5147 'goal_amount' => 100000.00,
5148 'is_email_receipt' => 1,
5150 'amount_block_is_active' => 1,
5151 'is_billing_required' => 0,
5152 ], $contributionPageParams))['id'];
5154 $priceSetID = PriceSet
::create()->setValues([
5155 'name' => 'quick config set',
5156 'title' => 'basic price set',
5157 'is_quick_config' => TRUE,
5159 ])->execute()->first()['id'];
5161 $priceFieldID = PriceField
::create()->setValues([
5162 'price_set_id' => $priceSetID,
5163 'name' => 'quick config field name',
5164 'label' => 'quick config field name',
5165 'html_type' => 'Radio',
5166 ])->execute()->first()['id'];
5167 $this->ids
['PriceSet']['basic'] = $priceSetID;
5168 $this->ids
['PriceField']['basic'] = $priceFieldID;
5169 $this->ids
['PriceFieldValue']['basic'] = PriceFieldValue
::create()->setValues([
5170 'price_field_id' => $priceFieldID,
5171 'name' => 'quick config price field',
5172 'label' => 'quick config price field',
5174 'financial_type_id:name' => 'Member Dues',
5175 ])->execute()->first()['id'];
5176 CRM_Price_BAO_PriceSet
::addTo('civicrm_contribution_page', $contributionPageID, $priceSetID);
5177 return $contributionPageID;