If a template contribution is updated we need to update the amount on the recurring...
[civicrm-core.git] / tests / phpunit / CRM / Contribute / BAO / ContributionTest.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7d61e75f 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
7d61e75f
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6e902643 11use Civi\Api4\Activity;
b89a5f6f 12use Civi\Api4\Contribution;
e26d0d27 13use Civi\Api4\PledgePayment;
6a488035 14
4cbe18b8
EM
15/**
16 * Class CRM_Contribute_BAO_ContributionTest
acb109b7 17 * @group headless
4cbe18b8 18 */
6a488035 19class CRM_Contribute_BAO_ContributionTest extends CiviUnitTestCase {
6a488035 20
6b60d32c 21 use CRMTraits_Financial_FinancialACLTrait;
53666099 22 use CRMTraits_Financial_PriceSetTrait;
6b60d32c 23
9cec8727
EM
24 /**
25 * Should financials be checked after the test but before tear down.
26 *
27 * @var bool
28 */
29 protected $isValidateFinancialsOnPostAssert = TRUE;
30
6a488035 31 /**
2449fe69 32 * Clean up after tests.
33 */
594a9328 34 public function tearDown(): void {
2449fe69 35 $this->quickCleanUpFinancialEntities();
2449fe69 36 parent::tearDown();
37 }
38
39 /**
40 * Test create method (create and update modes).
3fc37a30 41 *
42 * @throws \CRM_Core_Exception
6a488035 43 */
00be9182 44 public function testCreate() {
2449fe69 45 $contactId = $this->individualCreate();
6a488035 46
9099cab3 47 $params = [
6a488035
TO
48 'contact_id' => $contactId,
49 'currency' => 'USD',
92915c55 50 'financial_type_id' => 1,
6a488035
TO
51 'contribution_status_id' => 1,
52 'payment_instrument_id' => 1,
53 'source' => 'STUDENT',
54 'receive_date' => '20080522000000',
55 'receipt_date' => '20080522000000',
56 'non_deductible_amount' => 0.00,
57 'total_amount' => 200.00,
58 'fee_amount' => 5,
59 'net_amount' => 195,
60 'trxn_id' => '22ereerwww444444',
61 'invoice_id' => '86ed39c9e9ee6ef6031621ce0eafe7eb81',
62 'thankyou_date' => '20080522',
d0c97775 63 'sequential' => TRUE,
9099cab3 64 ];
6a488035 65
d0c97775 66 $contribution = $this->callAPISuccess('Contribution', 'create', $params)['values'][0];
6a488035 67
d0c97775 68 $this->assertEquals($params['trxn_id'], $contribution['trxn_id'], 'Check for transaction id creation.');
69 $this->assertEquals($contactId, $contribution['contact_id'], 'Check for contact id creation.');
6a488035
TO
70
71 //update contribution amount
d0c97775 72 $params['id'] = $contribution['id'];
6a488035
TO
73 $params['fee_amount'] = 10;
74 $params['net_amount'] = 190;
75
d0c97775 76 $contribution = $this->callAPISuccess('Contribution', 'create', $params)['values'][0];
6a488035 77
d0c97775 78 $this->assertEquals($params['trxn_id'], $contribution['trxn_id'], 'Check for transcation id .');
79 $this->assertEquals($params['net_amount'], $contribution['net_amount'], 'Check for Amount updation.');
6a488035
TO
80 }
81
82 /**
f43ac8d8 83 * Create() method with custom data.
6a488035 84 */
00be9182 85 public function testCreateWithCustomData() {
2449fe69 86 $contactId = $this->individualCreate();
6a488035
TO
87
88 //create custom data
9099cab3 89 $customGroup = $this->customGroupCreate(['extends' => 'Contribution']);
2449fe69 90 $customGroupID = $customGroup['id'];
91 $customGroup = $customGroup['values'][$customGroupID];
92
9099cab3 93 $fields = [
6a488035
TO
94 'label' => 'testFld',
95 'data_type' => 'String',
96 'html_type' => 'Text',
97 'is_active' => 1,
2449fe69 98 'custom_group_id' => $customGroupID,
9099cab3 99 ];
6a488035
TO
100 $customField = CRM_Core_BAO_CustomField::create($fields);
101
9099cab3 102 $params = [
6a488035
TO
103 'contact_id' => $contactId,
104 'currency' => 'USD',
92915c55 105 'financial_type_id' => 1,
6a488035
TO
106 'contribution_status_id' => 1,
107 'payment_instrument_id' => 1,
108 'source' => 'STUDENT',
109 'receive_date' => '20080522000000',
110 'receipt_date' => '20080522000000',
111 'id' => NULL,
112 'non_deductible_amount' => 0.00,
113 'total_amount' => 200.00,
114 'fee_amount' => 5,
115 'net_amount' => 195,
116 'trxn_id' => '22ereerwww322323',
117 'invoice_id' => '22ed39c9e9ee6ef6031621ce0eafe6da70',
118 'thankyou_date' => '20080522',
d0c97775 119 'skipCleanMoney' => TRUE,
9099cab3 120 ];
6a488035 121
9099cab3
CW
122 $params['custom'] = [
123 $customField->id => [
124 -1 => [
6a488035
TO
125 'value' => 'Test custom value',
126 'type' => 'String',
127 'custom_field_id' => $customField->id,
2449fe69 128 'custom_group_id' => $customGroupID,
129 'table_name' => $customGroup['table_name'],
6a488035
TO
130 'column_name' => $customField->column_name,
131 'file_id' => NULL,
9099cab3
CW
132 ],
133 ],
134 ];
6a488035 135
2449fe69 136 $contribution = CRM_Contribute_BAO_Contribution::create($params);
6a488035
TO
137
138 // Check that the custom field value is saved
9099cab3 139 $customValueParams = [
6a488035
TO
140 'entityID' => $contribution->id,
141 'custom_' . $customField->id => 1,
9099cab3 142 ];
6a488035
TO
143 $values = CRM_Core_BAO_CustomValueTable::getValues($customValueParams);
144 $this->assertEquals('Test custom value', $values['custom_' . $customField->id], 'Check the custom field value');
145
146 $this->assertEquals($params['trxn_id'], $contribution->trxn_id, 'Check for transcation id creation.');
147 $this->assertEquals($contactId, $contribution->contact_id, 'Check for contact id for Conribution.');
6a488035
TO
148 }
149
d73f286e
SL
150 /**
151 * CRM-21026 Test ContributionCount after contribution created with disabled FT
152 */
153 public function testContributionCountDisabledFinancialType() {
154 $contactId = $this->individualCreate();
9099cab3 155 $financialType = [
d73f286e
SL
156 'name' => 'grassvariety1' . substr(sha1(rand()), 0, 7),
157 'is_reserved' => 0,
158 'is_active' => 0,
9099cab3 159 ];
d73f286e 160 $finType = $this->callAPISuccess('financial_type', 'create', $financialType);
9099cab3 161 $params = [
d73f286e
SL
162 'contact_id' => $contactId,
163 'currency' => 'USD',
164 'financial_type_id' => $finType['id'],
165 'contribution_status_id' => 1,
166 'payment_instrument_id' => 1,
167 'source' => 'STUDENT',
168 'receive_date' => '20080522000000',
169 'receipt_date' => '20080522000000',
170 'id' => NULL,
171 'non_deductible_amount' => 0.00,
172 'total_amount' => 200.00,
173 'fee_amount' => 5,
174 'net_amount' => 195,
175 'trxn_id' => '22ereerwww322323',
176 'invoice_id' => '22ed39c9e9ee6ef6031621ce0eafe6da70',
177 'thankyou_date' => '20080522',
9099cab3 178 ];
d0c97775 179 $this->callAPISuccess('Contribution', 'create', $params);
9099cab3 180 $this->callAPISuccess('financial_type', 'create', ['is_active' => 0, 'id' => $finType['id']]);
d73f286e
SL
181 $contributionCount = CRM_Contribute_BAO_Contribution::contributionCount($contactId);
182 $this->assertEquals(1, $contributionCount);
183 }
184
6a488035 185 /**
100fef9d 186 * DeleteContribution() method
6a488035 187 */
00be9182 188 public function testDeleteContribution() {
2449fe69 189 $contactId = $this->individualCreate();
6a488035 190
9099cab3 191 $params = [
6a488035
TO
192 'contact_id' => $contactId,
193 'currency' => 'USD',
92915c55 194 'financial_type_id' => 1,
6a488035
TO
195 'contribution_status_id' => 1,
196 'payment_instrument_id' => 1,
197 'source' => 'STUDENT',
198 'receive_date' => '20080522000000',
199 'receipt_date' => '20080522000000',
200 'id' => NULL,
201 'non_deductible_amount' => 0.00,
202 'total_amount' => 200.00,
203 'fee_amount' => 5,
204 'net_amount' => 195,
205 'trxn_id' => '33ereerwww322323',
206 'invoice_id' => '33ed39c9e9ee6ef6031621ce0eafe6da70',
207 'thankyou_date' => '20080522',
55ee9063 208 'sequential' => TRUE,
9099cab3 209 ];
6a488035 210
55ee9063 211 $contribution = $this->callAPISuccess('Contribution', 'create', $params)['values'][0];
6a488035 212
55ee9063 213 CRM_Contribute_BAO_Contribution::deleteContribution($contribution['id']);
6a488035 214
55ee9063 215 $this->assertDBNull('CRM_Contribute_DAO_Contribution', $contribution['trxn_id'],
6a488035
TO
216 'id', 'trxn_id', 'Database check for deleted Contribution.'
217 );
6a488035
TO
218 }
219
220 /**
2449fe69 221 * Create honor-contact method.
6a488035 222 */
2449fe69 223 public function testCreateAndGetHonorContact() {
6a488035 224 $firstName = 'John_' . substr(sha1(rand()), 0, 7);
92915c55
TO
225 $lastName = 'Smith_' . substr(sha1(rand()), 0, 7);
226 $email = "{$firstName}.{$lastName}@example.com";
6a488035 227
8381af80 228 //Get profile id of name honoree_individual used to create profileContact
229 $honoreeProfileId = NULL;
230 $ufGroupDAO = new CRM_Core_DAO_UFGroup();
231 $ufGroupDAO->name = 'honoree_individual';
232 if ($ufGroupDAO->find(TRUE)) {
233 $honoreeProfileId = $ufGroupDAO->id;
234 }
235
9099cab3 236 $params = [
8381af80 237 'prefix_id' => 3,
238 'first_name' => $firstName,
239 'last_name' => $lastName,
240 'email-1' => $email,
9099cab3
CW
241 ];
242 $softParam = ['soft_credit_type_id' => 1];
8381af80 243
7b4b0b68
SL
244 $null = [];
245 $honoreeContactId = CRM_Contact_BAO_Contact::createProfileContact($params, $null,
92915c55
TO
246 NULL, NULL, $honoreeProfileId
247 );
6a488035 248
8381af80 249 $this->assertDBCompareValue('CRM_Contact_DAO_Contact', $honoreeContactId, 'first_name', 'id', $firstName,
6a488035
TO
250 'Database check for created honor contact record.'
251 );
252 //create contribution on behalf of honary.
253
9099cab3 254 $contactId = $this->individualCreate(['first_name' => 'John', 'last_name' => 'Doe']);
6a488035 255
9099cab3 256 $param = [
6a488035
TO
257 'contact_id' => $contactId,
258 'currency' => 'USD',
92915c55 259 'financial_type_id' => 4,
6a488035
TO
260 'contribution_status_id' => 1,
261 'receive_date' => date('Ymd'),
262 'total_amount' => 66,
d0c97775 263 'sequential' => 1,
9099cab3 264 ];
6a488035 265
d0c97775 266 $contribution = $this->callAPISuccess('Contribution', 'create', $param)['values'][0];
267 $id = $contribution['id'];
2449fe69 268 $softParam['contact_id'] = $honoreeContactId;
8381af80 269 $softParam['contribution_id'] = $id;
d0c97775 270 $softParam['currency'] = $contribution['currency'];
271 $softParam['amount'] = $contribution['total_amount'];
8381af80 272
273 //Create Soft Contribution for honoree contact
274 CRM_Contribute_BAO_ContributionSoft::add($softParam);
275
276 $this->assertDBCompareValue('CRM_Contribute_DAO_ContributionSoft', $id, 'contact_id',
277 'contribution_id', $honoreeContactId, 'Check DB for honor contact of the contribution'
6a488035 278 );
e4f46be0 279 //get honorary information
8381af80 280 $getHonorContact = CRM_Contribute_BAO_Contribution::getHonorContacts($honoreeContactId);
9099cab3
CW
281 $this->assertEquals([
282 $id => [
4ff25fbb 283 'honor_type' => 'In Honor of',
2449fe69 284 'honorId' => $contactId,
285 'display_name' => 'Mr. John Doe II',
4ff25fbb 286 'type' => 'Event Fee',
287 'type_id' => '4',
288 'amount' => '$ 66.00',
289 'source' => NULL,
755ca72c 290 'receive_date' => date('Y-m-d 00:00:00'),
4ff25fbb 291 'contribution_status' => 'Completed',
9099cab3
CW
292 ],
293 ], $getHonorContact);
6a488035 294
8381af80 295 $this->assertDBCompareValue('CRM_Contact_DAO_Contact', $honoreeContactId, 'first_name', 'id', $firstName,
6a488035
TO
296 'Database check for created honor contact record.'
297 );
298
299 //get annual contribution information
300 $annual = CRM_Contribute_BAO_Contribution::annual($contactId);
301
d0c97775 302 $currencySymbol = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_Currency', CRM_Core_Config::singleton()->defaultCurrency, 'symbol', 'name');
6a488035
TO
303 $this->assertDBCompareValue('CRM_Contribute_DAO_Contribution', $id, 'total_amount',
304 'id', ltrim($annual[2], $currencySymbol), 'Check DB for total amount of the contribution'
305 );
6a488035
TO
306 }
307
6b60d32c 308 /**
309 * Test that financial type data is not added to the annual query if acls not enabled.
310 */
311 public function testAnnualQueryWithFinancialACLsEnabled() {
312 $this->enableFinancialACLs();
313 $this->createLoggedInUserWithFinancialACL();
314 $permittedFinancialType = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Donation');
315 $sql = CRM_Contribute_BAO_Contribution::getAnnualQuery([1, 2, 3]);
275686a3
SL
316 $this->assertStringContainsString('SUM(total_amount) as amount,', $sql);
317 $this->assertStringContainsString('WHERE b.contact_id IN (1,2,3)', $sql);
318 $this->assertStringContainsString('b.financial_type_id IN (' . $permittedFinancialType . ')', $sql);
6b60d32c 319
320 // Run it to make sure it's not bad sql.
321 CRM_Core_DAO::executeQuery($sql);
322 $this->disableFinancialACLs();
323 }
324
53666099 325 /**
326 * Test the annual query returns a correct result when multiple line items are present.
327 */
e211aedf 328 public function testAnnualWithMultipleLineItems(): void {
53666099 329 $contactID = $this->createLoggedInUserWithFinancialACL();
330 $this->createContributionWithTwoLineItemsAgainstPriceSet([
39b959db
SL
331 'contact_id' => $contactID,
332 ]
53666099 333 );
334 $this->enableFinancialACLs();
335 $sql = CRM_Contribute_BAO_Contribution::getAnnualQuery([$contactID]);
336 $result = CRM_Core_DAO::executeQuery($sql);
337 $result->fetch();
338 $this->assertEquals(300, $result->amount);
339 $this->assertEquals(1, $result->count);
340 $this->disableFinancialACLs();
341 }
342
6b60d32c 343 /**
344 * Test that financial type data is not added to the annual query if acls not enabled.
345 */
346 public function testAnnualQueryWithFinancialACLsDisabled() {
347 $sql = CRM_Contribute_BAO_Contribution::getAnnualQuery([1, 2, 3]);
275686a3
SL
348 $this->assertStringContainsString('SUM(total_amount) as amount,', $sql);
349 $this->assertStringContainsString('WHERE b.contact_id IN (1,2,3)', $sql);
350 $this->assertStringNotContainsString('b.financial_type_id', $sql);
6b60d32c 351 //$this->assertNotContains('line_item', $sql);
352 // Run it to make sure it's not bad sql.
353 CRM_Core_DAO::executeQuery($sql);
354 }
355
c77f8667 356 /**
357 * Test that financial type data is not added to the annual query if acls not enabled.
358 */
359 public function testAnnualQueryWithFinancialHook() {
9099cab3 360 $this->hookClass->setHook('civicrm_selectWhereClause', [$this, 'aclIdNoZero']);
c77f8667 361 $sql = CRM_Contribute_BAO_Contribution::getAnnualQuery([1, 2, 3]);
275686a3
SL
362 $this->assertStringContainsString('SUM(total_amount) as amount,', $sql);
363 $this->assertStringContainsString('WHERE b.contact_id IN (1,2,3)', $sql);
364 $this->assertStringContainsString('b.id NOT IN (0)', $sql);
365 $this->assertStringNotContainsString('b.financial_type_id', $sql);
c77f8667 366 CRM_Core_DAO::executeQuery($sql);
367 }
368
369 /**
370 * Add ACL denying values LIKE '0'.
371 *
372 * @param string $entity
373 * @param string $clauses
374 */
375 public function aclIdNoZero($entity, &$clauses) {
376 if ($entity != 'Contribution') {
377 return;
378 }
b9466f09 379 $clauses['id'] = 'NOT IN (0)';
c77f8667 380 }
381
6a488035 382 /**
eceb18cc 383 * Display sort name during.
b581842f 384 * Update multiple contributions
6a488035
TO
385 * sortName();
386 */
00be9182 387 public function testsortName() {
9099cab3 388 $params = [
6a488035
TO
389 'first_name' => 'Shane',
390 'last_name' => 'Whatson',
391 'contact_type' => 'Individual',
9099cab3 392 ];
6a488035
TO
393
394 $contact = CRM_Contact_BAO_Contact::add($params);
395
396 //Now check $contact is object of contact DAO..
397 $this->assertInstanceOf('CRM_Contact_DAO_Contact', $contact, 'Check for created object');
398
399 $contactId = $contact->id;
9099cab3 400 $param = [
6a488035
TO
401 'contact_id' => $contactId,
402 'currency' => 'USD',
92915c55 403 'financial_type_id' => 1,
6a488035
TO
404 'contribution_status_id' => 1,
405 'payment_instrument_id' => 1,
406 'source' => 'STUDENT',
407 'receive_date' => '20080522000000',
408 'receipt_date' => '20080522000000',
409 'id' => NULL,
410 'non_deductible_amount' => 0.00,
411 'total_amount' => 300.00,
412 'fee_amount' => 5,
413 'net_amount' => 295,
414 'trxn_id' => '22ereerwww323',
415 'invoice_id' => '22ed39c9e9ee621ce0eafe6da70',
416 'thankyou_date' => '20080522',
d0c97775 417 'sequential' => TRUE,
9099cab3 418 ];
6a488035 419
d0c97775 420 $contribution = $this->callAPISuccess('Contribution', 'create', $param)['values'][0];
6a488035 421
d0c97775 422 $this->assertEquals($param['trxn_id'], $contribution['trxn_id'], 'Check for transcation id creation.');
423 $this->assertEquals($contactId, $contribution['contact_id'], 'Check for contact id creation.');
6a488035 424
b581842f 425 //display sort name during Update multiple contributions
d0c97775 426 $sortName = CRM_Contribute_BAO_Contribution::sortName($contribution['id']);
6a488035
TO
427
428 $this->assertEquals('Whatson, Shane', $sortName, 'Check for sort name.');
6a488035
TO
429 }
430
431 /**
eceb18cc 432 * Add premium during online Contribution.
6a488035
TO
433 *
434 * AddPremium();
435 */
00be9182 436 public function testAddPremium() {
2449fe69 437 $contactId = $this->individualCreate();
6a488035 438
9099cab3 439 $params = [
6a488035
TO
440 'name' => 'TEST Premium',
441 'sku' => 111,
442 'imageOption' => 'noImage',
443 'MAX_FILE_SIZE' => 2097152,
444 'price' => 100.00,
445 'cost' => 90.00,
446 'min_contribution' => 100,
447 'is_active' => 1,
9099cab3 448 ];
32f27499 449 $premium = CRM_Contribute_BAO_Product::create($params);
6a488035
TO
450
451 $this->assertEquals('TEST Premium', $premium->name, 'Check for premium name.');
452
9099cab3 453 $contributionParams = [
6a488035
TO
454 'contact_id' => $contactId,
455 'currency' => 'USD',
92915c55 456 'financial_type_id' => 1,
6a488035
TO
457 'contribution_status_id' => 1,
458 'payment_instrument_id' => 1,
459 'source' => 'STUDENT',
460 'receive_date' => '20080522000000',
461 'receipt_date' => '20080522000000',
462 'id' => NULL,
463 'non_deductible_amount' => 0.00,
464 'total_amount' => 300.00,
465 'fee_amount' => 5,
466 'net_amount' => 295,
467 'trxn_id' => '33erdfrwvw434',
468 'invoice_id' => '98ed34f7u9hh672ce0eafe8fb92',
469 'thankyou_date' => '20080522',
d0c97775 470 'sequential' => TRUE,
9099cab3 471 ];
a275d4b6 472 $contribution = $this->callAPISuccess('Contribution', 'create', $contributionParams)['values'][0];
6a488035 473
a275d4b6 474 $this->assertEquals($contributionParams['trxn_id'], $contribution['trxn_id'], 'Check for transcation id creation.');
d0c97775 475 $this->assertEquals($contactId, $contribution['contact_id'], 'Check for contact id creation.');
6a488035
TO
476
477 //parameter for adding premium to contribution
9099cab3 478 $data = [
6a488035 479 'product_id' => $premium->id,
d0c97775 480 'contribution_id' => $contribution['id'],
6a488035
TO
481 'product_option' => NULL,
482 'quantity' => 1,
9099cab3 483 ];
6a488035
TO
484 $contributionProduct = CRM_Contribute_BAO_Contribution::addPremium($data);
485 $this->assertEquals($contributionProduct->product_id, $premium->id, 'Check for Product id .');
486
487 //Delete Product
37828d4f 488 CRM_Contribute_BAO_Product::del($premium->id);
6a488035
TO
489 $this->assertDBNull('CRM_Contribute_DAO_Product', $premium->name,
490 'id', 'name', 'Database check for deleted Product.'
491 );
6a488035
TO
492 }
493
494 /**
fe482240 495 * Check duplicate contribution id.
6a488035
TO
496 * during the contribution import
497 * checkDuplicateIds();
498 */
00be9182 499 public function testcheckDuplicateIds() {
2449fe69 500 $contactId = $this->individualCreate();
6a488035 501
9099cab3 502 $param = [
6a488035
TO
503 'contact_id' => $contactId,
504 'currency' => 'USD',
92915c55 505 'financial_type_id' => 1,
6a488035
TO
506 'contribution_status_id' => 1,
507 'payment_instrument_id' => 1,
508 'source' => 'STUDENT',
509 'receive_date' => '20080522000000',
510 'receipt_date' => '20080522000000',
511 'id' => NULL,
512 'non_deductible_amount' => 0.00,
513 'total_amount' => 300.00,
514 'fee_amount' => 5,
515 'net_amount' => 295,
516 'trxn_id' => '76ereeswww835',
517 'invoice_id' => '93ed39a9e9hd621bs0eafe3da82',
518 'thankyou_date' => '20080522',
d0c97775 519 'sequential' => TRUE,
9099cab3 520 ];
6a488035 521
d0c97775 522 $contribution = $this->callAPISuccess('Contribution', 'create', $param)['values'][0];
6a488035 523
d0c97775 524 $this->assertEquals($param['trxn_id'], $contribution['trxn_id'], 'Check for transcation id creation.');
525 $this->assertEquals($contactId, $contribution['contact_id'], 'Check for contact id creation.');
9099cab3 526 $data = [
d0c97775 527 'id' => $contribution['id'],
528 'trxn_id' => $contribution['trxn_id'],
529 'invoice_id' => $contribution['invoice_id'],
9099cab3 530 ];
6a488035 531 $contributionID = CRM_Contribute_BAO_Contribution::checkDuplicateIds($data);
d0c97775 532 $this->assertEquals($contributionID, $contribution['id'], 'Check for duplicate transcation id .');
6a488035 533 }
96025800 534
b04e4b11
PN
535 /**
536 * Create() method (create and update modes).
537 */
538 public function testIsPaymentFlag() {
2449fe69 539 $contactId = $this->individualCreate();
b04e4b11 540
d0c97775 541 $params = [
b04e4b11
PN
542 'contact_id' => $contactId,
543 'currency' => 'USD',
544 'financial_type_id' => 1,
545 'contribution_status_id' => 1,
546 'payment_instrument_id' => 1,
547 'source' => 'STUDENT',
548 'receive_date' => '20080522000000',
549 'receipt_date' => '20080522000000',
550 'non_deductible_amount' => 0.00,
551 'total_amount' => 200.00,
552 'fee_amount' => 5,
553 'net_amount' => 195,
554 'trxn_id' => '22ereerwww4444xx',
555 'invoice_id' => '86ed39c9e9ee6ef6541621ce0eafe7eb81',
556 'thankyou_date' => '20080522',
d0c97775 557 'sequential' => TRUE,
558 ];
559 $contribution = $this->callAPISuccess('Contribution', 'create', $params)['values'][0];
b04e4b11 560
d0c97775 561 $this->assertEquals($params['trxn_id'], $contribution['trxn_id'], 'Check for transcation id creation.');
562 $this->assertEquals($contactId, $contribution['contact_id'], 'Check for contact id creation.');
b04e4b11 563
9099cab3 564 $trxnArray = [
b04e4b11
PN
565 'trxn_id' => $params['trxn_id'],
566 'is_payment' => 1,
9099cab3
CW
567 ];
568 $defaults = [];
b04e4b11 569 $financialTrxn = CRM_Core_BAO_FinancialTrxn::retrieve($trxnArray, $defaults);
4ba3c75f 570 $this->assertEquals(1, $financialTrxn->N, 'Mismatch count for is payment flag.');
b04e4b11 571 //update contribution amount
d0c97775 572 $params['id'] = $contribution['id'];
b04e4b11 573 $params['total_amount'] = 150;
d0c97775 574 $contribution = $this->callAPISuccess('Contribution', 'create', $params)['values'][0];
b04e4b11 575
d0c97775 576 $this->assertEquals($params['trxn_id'], $contribution['trxn_id'], 'Check for transcation id .');
577 $this->assertEquals($params['total_amount'], $contribution['total_amount'], 'Check for Amount updation.');
9099cab3 578 $trxnArray = [
b04e4b11
PN
579 'trxn_id' => $params['trxn_id'],
580 'is_payment' => 1,
9099cab3
CW
581 ];
582 $defaults = [];
b04e4b11 583 $financialTrxn = CRM_Core_BAO_FinancialTrxn::retrieve($trxnArray, $defaults);
4ba3c75f 584 $this->assertEquals(2, $financialTrxn->N, 'Mismatch count for is payment flag.');
b04e4b11
PN
585 $trxnArray['is_payment'] = 0;
586 $financialTrxn = CRM_Core_BAO_FinancialTrxn::retrieve($trxnArray, $defaults);
4ba3c75f 587 $this->assertEquals(1, $financialTrxn->N, 'Mismatch count for is payment flag.');
b04e4b11
PN
588 }
589
a387acc9
PN
590 /**
591 * Create() method (create and update modes).
592 */
593 public function testIsPaymentFlagForPending() {
2449fe69 594 $contactId = $this->individualCreate();
a387acc9 595
9099cab3 596 $params = [
a387acc9
PN
597 'contact_id' => $contactId,
598 'currency' => 'USD',
599 'financial_type_id' => 1,
600 'contribution_status_id' => 2,
601 'payment_instrument_id' => 1,
602 'source' => 'STUDENT',
603 'is_pay_later' => 1,
604 'receive_date' => '20080522000000',
605 'receipt_date' => '20080522000000',
606 'non_deductible_amount' => 0.00,
607 'total_amount' => 200.00,
608 'fee_amount' => 5,
609 'net_amount' => 195,
610 'trxn_id' => '22ereerwww4444yy',
611 'invoice_id' => '86ed39c9e9yy6ef6541621ce0eafe7eb81',
612 'thankyou_date' => '20080522',
d0c97775 613 'sequential' => TRUE,
9099cab3 614 ];
a387acc9 615
d0c97775 616 $contribution = $this->callAPISuccess('Contribution', 'create', $params)['values'][0];
a387acc9 617
d0c97775 618 $this->assertEquals($params['trxn_id'], $contribution['trxn_id'], 'Check for transaction id creation.');
619 $this->assertEquals($contactId, $contribution['contact_id'], 'Check for contact id creation.');
a387acc9 620
9099cab3 621 $trxnArray = [
a387acc9
PN
622 'trxn_id' => $params['trxn_id'],
623 'is_payment' => 0,
9099cab3
CW
624 ];
625 $defaults = [];
a387acc9
PN
626 $financialTrxn = CRM_Core_BAO_FinancialTrxn::retrieve($trxnArray, $defaults);
627 $this->assertEquals(2, $financialTrxn->N, 'Mismatch count for is payment flag.');
628 $trxnArray['is_payment'] = 1;
629 $financialTrxn = CRM_Core_BAO_FinancialTrxn::retrieve($trxnArray, $defaults);
630 $this->assertEquals(NULL, $financialTrxn, 'Mismatch count for is payment flag.');
631 //update contribution amount
d0c97775 632 $params['id'] = $contribution['id'];
a387acc9
PN
633 $params['contribution_status_id'] = 1;
634
d0c97775 635 $contribution = $this->callAPISuccess('Contribution', 'create', $params)['values'][0];
a387acc9 636
d0c97775 637 $this->assertEquals($params['trxn_id'], $contribution['trxn_id'], 'Check for transcation id .');
638 $this->assertEquals($params['contribution_status_id'], $contribution['contribution_status_id'], 'Check for status updation.');
9099cab3 639 $trxnArray = [
a387acc9
PN
640 'trxn_id' => $params['trxn_id'],
641 'is_payment' => 1,
9099cab3
CW
642 ];
643 $defaults = [];
a387acc9
PN
644 $financialTrxn = CRM_Core_BAO_FinancialTrxn::retrieve($trxnArray, $defaults);
645 $this->assertEquals(1, $financialTrxn->N, 'Mismatch count for is payment flag.');
646 $trxnArray['is_payment'] = 0;
647 $financialTrxn = CRM_Core_BAO_FinancialTrxn::retrieve($trxnArray, $defaults);
648 $this->assertEquals(2, $financialTrxn->N, 'Mismatch count for is payment flag.');
a387acc9
PN
649 }
650
0a5651eb
PN
651 /**
652 * checks db values for financial item
653 */
624195c8 654 public function checkItemValues($contribution) {
876b8ab0 655 $toFinancialAccount = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount(4, 'Accounts Receivable Account is');
2449fe69 656 $query = "SELECT eft1.entity_id, ft.total_amount, eft1.amount FROM civicrm_financial_trxn ft INNER JOIN civicrm_entity_financial_trxn eft ON (eft.financial_trxn_id = ft.id AND eft.entity_table = 'civicrm_contribution')
eec619df
PN
657INNER JOIN civicrm_entity_financial_trxn eft1 ON (eft1.financial_trxn_id = eft.financial_trxn_id AND eft1.entity_table = 'civicrm_financial_item')
658WHERE eft.entity_id = %1 AND ft.to_financial_account_id <> %2";
659
9099cab3
CW
660 $queryParams[1] = [$contribution->id, 'Integer'];
661 $queryParams[2] = [$toFinancialAccount, 'Integer'];
eec619df
PN
662
663 $dao = CRM_Core_DAO::executeQuery($query, $queryParams);
9099cab3 664 $amounts = [100.00, 50.00];
eec619df
PN
665 while ($dao->fetch()) {
666 $this->assertEquals(150.00, $dao->total_amount, 'Mismatch of total amount paid.');
19084b68 667 $this->assertEquals($dao->amount, array_pop($amounts), 'Mismatch of amount proportionally assigned to financial item');
eec619df 668 }
eec619df
PN
669 }
670
0a5651eb
PN
671 /**
672 * assignProportionalLineItems() method (add and edit modes of participant)
3fc37a30 673 *
674 * @throws \CRM_Core_Exception
675 * @throws \CiviCRM_API3_Exception
0a5651eb 676 */
9cec8727
EM
677 public function testAssignProportionalLineItems(): void {
678 // This test doesn't seem to manage financials properly, possibly by design
679 $this->isValidateFinancialsOnPostAssert = FALSE;
3fc37a30 680 $contribution = $this->addParticipantWithContribution();
681 // Delete existing financial_trxns. This is because we are testing a code flow we
682 // want to deprecate & remove & the test relies on bad data asa starting point.
683 // End goal is the Order.create->Payment.create flow.
684 CRM_Core_DAO::executeQuery('DELETE FROM civicrm_entity_financial_trxn WHERE entity_table = "civicrm_financial_item"');
9099cab3 685 $params = [
0a5651eb
PN
686 'contribution_id' => $contribution->id,
687 'total_amount' => 150.00,
9099cab3 688 ];
0a5651eb
PN
689 $trxn = new CRM_Financial_DAO_FinancialTrxn();
690 $trxn->orderBy('id DESC');
691 $trxn->find(TRUE);
8de1ade9 692 CRM_Contribute_BAO_Contribution::assignProportionalLineItems($params, $trxn->id, $contribution->total_amount);
0a5651eb
PN
693 $this->checkItemValues($contribution);
694 }
695
eec619df
PN
696 /**
697 * Add participant with contribution
698 *
57a7aa8a 699 * @return CRM_Contribute_BAO_Contribution
3fc37a30 700 *
701 * @throws \CRM_Core_Exception
702 * @throws \CiviCRM_API3_Exception
eec619df 703 */
78c99516 704 public function addParticipantWithContribution() {
eec619df 705 // creating price set, price field
2449fe69 706 $this->_contactId = $this->individualCreate();
3fc37a30 707 $event = $this->eventCreatePaid([]);
2449fe69 708 $this->_eventId = $event['id'];
57a7aa8a 709 $priceSetID = $this->ids['PriceSet']['event'];
9099cab3 710 $paramsField = [
eec619df
PN
711 'label' => 'Price Field',
712 'name' => CRM_Utils_String::titleToVar('Price Field'),
713 'html_type' => 'CheckBox',
9099cab3
CW
714 'option_label' => ['1' => 'Price Field 1', '2' => 'Price Field 2'],
715 'option_value' => ['1' => 100, '2' => 200],
716 'option_name' => ['1' => 'Price Field 1', '2' => 'Price Field 2'],
717 'option_weight' => ['1' => 1, '2' => 2],
718 'option_amount' => ['1' => 100, '2' => 200],
eec619df
PN
719 'is_display_amounts' => 1,
720 'weight' => 1,
721 'options_per_line' => 1,
9099cab3 722 'is_active' => ['1' => 1, '2' => 1],
57a7aa8a 723 'price_set_id' => $priceSetID,
eec619df
PN
724 'is_enter_qty' => 1,
725 'financial_type_id' => CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialType', 'Event Fee', 'id', 'name'),
9099cab3 726 ];
eec619df 727 $priceField = CRM_Price_BAO_PriceField::create($paramsField);
9099cab3 728 $eventParams = [
eec619df
PN
729 'id' => $this->_eventId,
730 'financial_type_id' => 4,
731 'is_monetary' => 1,
9099cab3 732 ];
eec619df 733 CRM_Event_BAO_Event::create($eventParams);
57a7aa8a 734 CRM_Price_BAO_PriceSet::addTo('civicrm_event', $this->_eventId, $priceSetID);
e4ba8498 735
9099cab3
CW
736 $priceFields = $this->callAPISuccess('PriceFieldValue', 'get', ['price_field_id' => $priceField->id]);
737 $participantParams = [
eec619df
PN
738 'financial_type_id' => 4,
739 'event_id' => $this->_eventId,
740 'role_id' => 1,
741 'status_id' => 14,
742 'fee_currency' => 'USD',
743 'contact_id' => $this->_contactId,
9099cab3 744 ];
eec619df 745 $participant = CRM_Event_BAO_Participant::add($participantParams);
9099cab3 746 $contributionParams = [
3fc37a30 747 'total_amount' => 300,
eec619df
PN
748 'currency' => 'USD',
749 'contact_id' => $this->_contactId,
750 'financial_type_id' => 4,
3fc37a30 751 'contribution_status_id' => 'Pending',
eec619df 752 'participant_id' => $participant->id,
d0c97775 753 'sequential' => TRUE,
3fc37a30 754 'api.Payment.create' => ['total_amount' => 150],
9099cab3 755 ];
eec619df
PN
756
757 foreach ($priceFields['values'] as $key => $priceField) {
9099cab3 758 $lineItems[1][$key] = [
eec619df
PN
759 'price_field_id' => $priceField['price_field_id'],
760 'price_field_value_id' => $priceField['id'],
761 'label' => $priceField['label'],
762 'field_title' => $priceField['label'],
763 'qty' => 1,
764 'unit_price' => $priceField['amount'],
765 'line_total' => $priceField['amount'],
766 'financial_type_id' => $priceField['financial_type_id'],
9099cab3 767 ];
eec619df
PN
768 }
769 $contributionParams['line_item'] = $lineItems;
d0c97775 770 $contribution = $this->callAPISuccess('Contribution', 'create', $contributionParams)['values'][0];
eec619df 771
9099cab3 772 $paymentParticipant = [
eec619df 773 'participant_id' => $participant->id,
d0c97775 774 'contribution_id' => $contribution['id'],
9099cab3 775 ];
a5750507 776 CRM_Event_BAO_ParticipantPayment::create($paymentParticipant);
eec619df 777
d0c97775 778 $contributionObject = new CRM_Contribute_BAO_Contribution();
779 $contributionObject->id = $contribution['id'];
780 $contributionObject->find(TRUE);
781
3fc37a30 782 return $contributionObject;
eec619df
PN
783 }
784
88f7a518 785 /**
6e902643 786 * Test activity amount updates activity subject.
88f7a518
E
787 */
788 public function testActivityCreate() {
789 $contactId = $this->individualCreate();
88f7a518 790
9099cab3 791 $params = [
88f7a518
E
792 'contact_id' => $contactId,
793 'currency' => 'USD',
794 'financial_type_id' => 1,
795 'contribution_status_id' => 1,
796 'payment_instrument_id' => 1,
797 'source' => 'STUDENT',
798 'receive_date' => '20080522000000',
799 'receipt_date' => '20080522000000',
800 'non_deductible_amount' => 0.00,
801 'total_amount' => 100.00,
88f7a518
E
802 'invoice_id' => '86ed39c9e9ee6ef6031621ce0eafe7eb81',
803 'thankyou_date' => '20160519',
d0c97775 804 'sequential' => 1,
9099cab3 805 ];
88f7a518 806
d0c97775 807 $contribution = $this->callAPISuccess('Contribution', 'create', $params)['values'][0];
88f7a518 808
d0c97775 809 $this->assertEquals($params['total_amount'], $contribution['total_amount'], 'Check for total amount in contribution.');
810 $this->assertEquals($contactId, $contribution['contact_id'], 'Check for contact id creation.');
6e902643 811 $activityWhere = [
812 ['source_record_id', '=', $contribution['id']],
813 ['activity_type_id:name', '=', 'Contribution'],
9099cab3 814 ];
6e902643 815 $activity = Activity::get()->setWhere($activityWhere)->setSelect(['source_record_id', 'subject'])->execute()->first();
88f7a518 816
6e902643 817 $this->assertEquals($contribution['id'], $activity['source_record_id'], 'Check for activity associated with contribution.');
818 $this->assertEquals('$ 100.00 - STUDENT', $activity['subject'], 'Check for total amount in activity.');
88f7a518 819
d0c97775 820 $params['id'] = $contribution['id'];
88f7a518 821 $params['total_amount'] = 200;
6e902643 822 $params['campaign_id'] = $this->campaignCreate();
88f7a518 823
d0c97775 824 $contribution = $this->callAPISuccess('Contribution', 'create', $params)['values'][0];
88f7a518 825
d0c97775 826 $this->assertEquals($params['total_amount'], $contribution['total_amount'], 'Check for total amount in contribution.');
827 $this->assertEquals($contactId, $contribution['contact_id'], 'Check for contact id creation.');
88f7a518
E
828
829 // Retrieve activity again.
6e902643 830 $activity = Activity::get()->setWhere($activityWhere)->setSelect(['source_record_id', 'subject', 'campaign_id'])->execute()->first();
88f7a518 831
6e902643 832 $this->assertEquals($contribution['id'], $activity['source_record_id'], 'Check for activity associated with contribution.');
833 $this->assertEquals('$ 200.00 - STUDENT', $activity['subject'], 'Check for total amount in activity.');
834 $this->assertEquals($params['campaign_id'], $activity['campaign_id']);
88f7a518
E
835 }
836
5c3d600f
PN
837 /**
838 * Test allowUpdateRevenueRecognitionDate.
af595ac2 839 *
840 * @throws \CRM_Core_Exception
5c3d600f
PN
841 */
842 public function testAllowUpdateRevenueRecognitionDate() {
843 $contactId = $this->individualCreate();
9099cab3 844 $params = [
5c3d600f
PN
845 'contact_id' => $contactId,
846 'receive_date' => '2010-01-20',
847 'total_amount' => 100,
4303ea89 848 'financial_type_id' => 4,
af595ac2 849 'contribution_status_id' => 'Pending',
9099cab3 850 ];
af595ac2 851 $order = $this->callAPISuccess('Order', 'create', $params);
5c3d600f
PN
852 $allowUpdate = CRM_Contribute_BAO_Contribution::allowUpdateRevenueRecognitionDate($order['id']);
853 $this->assertTrue($allowUpdate);
854
855 $event = $this->eventCreate();
9099cab3 856 $params = [
5c3d600f
PN
857 'contact_id' => $contactId,
858 'receive_date' => '2010-01-20',
859 'total_amount' => 300,
8484a5f0 860 'financial_type_id' => $this->getFinancialTypeId('Event Fee'),
af595ac2 861 'contribution_status_id' => 'Pending',
9099cab3 862 ];
5c3d600f
PN
863 $priceFields = $this->createPriceSet('event', $event['id']);
864 foreach ($priceFields['values'] as $key => $priceField) {
9099cab3 865 $lineItems[$key] = [
5c3d600f
PN
866 'price_field_id' => $priceField['price_field_id'],
867 'price_field_value_id' => $priceField['id'],
868 'label' => $priceField['label'],
869 'field_title' => $priceField['label'],
870 'qty' => 1,
871 'unit_price' => $priceField['amount'],
872 'line_total' => $priceField['amount'],
873 'financial_type_id' => $priceField['financial_type_id'],
874 'entity_table' => 'civicrm_participant',
9099cab3 875 ];
5c3d600f 876 }
9099cab3 877 $params['line_items'][] = [
5c3d600f 878 'line_item' => $lineItems,
9099cab3 879 'params' => [
5c3d600f
PN
880 'contact_id' => $contactId,
881 'event_id' => $event['id'],
882 'status_id' => 1,
883 'role_id' => 1,
884 'register_date' => '2007-07-21 00:00:00',
885 'source' => 'Online Event Registration: API Testing',
9099cab3
CW
886 ],
887 ];
af595ac2 888 $order = $this->callAPISuccess('Order', 'create', $params);
5c3d600f
PN
889 $allowUpdate = CRM_Contribute_BAO_Contribution::allowUpdateRevenueRecognitionDate($order['id']);
890 $this->assertFalse($allowUpdate);
891
9099cab3 892 $params = [
5c3d600f
PN
893 'contact_id' => $contactId,
894 'receive_date' => '2010-01-20',
895 'total_amount' => 200,
8484a5f0 896 'financial_type_id' => $this->getFinancialTypeId('Member Dues'),
af595ac2 897 'contribution_status_id' => 'Pending',
9099cab3 898 ];
5c3d600f
PN
899 $membershipType = $this->membershipTypeCreate();
900 $priceFields = $this->createPriceSet();
9099cab3 901 $lineItems = [];
5c3d600f 902 foreach ($priceFields['values'] as $key => $priceField) {
9099cab3 903 $lineItems[$key] = [
5c3d600f
PN
904 'price_field_id' => $priceField['price_field_id'],
905 'price_field_value_id' => $priceField['id'],
906 'label' => $priceField['label'],
907 'field_title' => $priceField['label'],
908 'qty' => 1,
909 'unit_price' => $priceField['amount'],
910 'line_total' => $priceField['amount'],
911 'financial_type_id' => $priceField['financial_type_id'],
912 'entity_table' => 'civicrm_membership',
913 'membership_type_id' => $membershipType,
9099cab3 914 ];
5c3d600f 915 }
9099cab3
CW
916 $params['line_items'][] = [
917 'line_item' => [array_pop($lineItems)],
918 'params' => [
5c3d600f
PN
919 'contact_id' => $contactId,
920 'membership_type_id' => $membershipType,
921 'join_date' => '2006-01-21',
922 'start_date' => '2006-01-21',
923 'end_date' => '2006-12-21',
924 'source' => 'Payment',
925 'is_override' => 1,
926 'status_id' => 1,
9099cab3
CW
927 ],
928 ];
af595ac2 929 $order = $this->callAPISuccess('Order', 'create', $params);
5c3d600f
PN
930 $allowUpdate = CRM_Contribute_BAO_Contribution::allowUpdateRevenueRecognitionDate($order['id']);
931 $this->assertFalse($allowUpdate);
932 }
933
d934a732
PN
934 /**
935 * Test recording of amount with comma separator.
b4c48831 936 *
937 * @throws \CRM_Core_Exception
d934a732
PN
938 */
939 public function testCommaSeparatorAmount() {
940 $contactId = $this->individualCreate();
941
9099cab3 942 $params = [
d934a732
PN
943 'contact_id' => $contactId,
944 'currency' => 'USD',
945 'financial_type_id' => 1,
b4c48831 946 'contribution_status_id' => 'Pending',
d934a732
PN
947 'payment_instrument_id' => 1,
948 'receive_date' => '20080522000000',
949 'receipt_date' => '20080522000000',
b4c48831 950 'total_amount' => '20,000.00',
951 'api.Payment.create' => ['total_amount' => '8,000.00'],
2c35902f 952 'skipCleanMoney' => FALSE,
9099cab3 953 ];
d934a732 954
b4c48831 955 $contribution = $this->callAPISuccess('Order', 'create', $params);
d0c97775 956 $lastFinancialTrxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($contribution['id'], 'DESC');
d934a732
PN
957 $financialTrxn = $this->callAPISuccessGetSingle(
958 'FinancialTrxn',
9099cab3 959 [
d934a732 960 'id' => $lastFinancialTrxnId['financialTrxnId'],
9099cab3
CW
961 'return' => ['total_amount'],
962 ]
d934a732 963 );
b4c48831 964 $this->assertEquals($financialTrxn['total_amount'], 8000, 'Invalid amount.');
d934a732
PN
965 }
966
adbc354b
PN
967 /**
968 * Test for function getSalesTaxFinancialAccounts().
969 */
970 public function testgetSalesTaxFinancialAccounts() {
971 $this->enableTaxAndInvoicing();
972 $financialType = $this->createFinancialType();
28d44c71 973 $financialAccount = $this->addTaxAccountToFinancialType($financialType['id']);
9099cab3 974 $expectedResult = [$financialAccount->financial_account_id => $financialAccount->financial_account_id];
adbc354b 975 $financialType = $this->createFinancialType();
28d44c71 976 $financialAccount = $this->addTaxAccountToFinancialType($financialType['id']);
adbc354b
PN
977 $expectedResult[$financialAccount->financial_account_id] = $financialAccount->financial_account_id;
978 $salesTaxFinancialAccount = CRM_Contribute_BAO_Contribution::getSalesTaxFinancialAccounts();
979 $this->assertTrue(($salesTaxFinancialAccount == $expectedResult), 'Function returned wrong values.');
980 }
981
0409b821
PN
982 /**
983 * Test for function createProportionalEntry().
83644f47 984 *
985 * @param string $thousandSeparator
986 * punctuation used to refer to thousands.
987 *
988 * @dataProvider getThousandSeparators
0409b821 989 */
83644f47 990 public function testCreateProportionalEntry($thousandSeparator) {
991 $this->setCurrencySeparators($thousandSeparator);
0b577f4b 992 [$contribution, $financialAccount] = $this->createContributionWithTax();
9099cab3 993 $params = [
0409b821
PN
994 'total_amount' => 55,
995 'to_financial_account_id' => $financialAccount->financial_account_id,
996 'payment_instrument_id' => 1,
997 'trxn_date' => date('Ymd'),
998 'status_id' => 1,
0409b821 999 'entity_id' => $contribution['id'],
9099cab3 1000 ];
3137781d 1001 $financialTrxn = $this->callAPISuccess('FinancialTrxn', 'create', $params);
9099cab3 1002 $entityParams = [
0409b821
PN
1003 'contribution_total_amount' => $contribution['total_amount'],
1004 'trxn_total_amount' => 55,
1005 'line_item_amount' => 100,
9099cab3 1006 ];
0409b821 1007 $previousLineItem = CRM_Financial_BAO_FinancialItem::getPreviousFinancialItem($contribution['id']);
9099cab3 1008 $eftParams = [
0409b821 1009 'entity_table' => 'civicrm_financial_item',
cf28d075 1010 'entity_id' => $previousLineItem['id'],
3137781d 1011 'financial_trxn_id' => (string) $financialTrxn['id'],
9099cab3 1012 ];
0409b821 1013 CRM_Contribute_BAO_Contribution::createProportionalEntry($entityParams, $eftParams);
9099cab3 1014 $trxnTestArray = array_merge($eftParams, [
3137781d 1015 'amount' => '50.00',
9099cab3 1016 ]);
cf28d075 1017 $this->callAPISuccessGetSingle('EntityFinancialTrxn', $eftParams, $trxnTestArray);
0409b821
PN
1018 }
1019
c364c544
MW
1020 /**
1021 * Test for function createProportionalEntry with zero amount().
1022 *
1023 * @param string $thousandSeparator
1024 * punctuation used to refer to thousands.
1025 *
0b577f4b
EM
1026 * @throws \CRM_Core_Exception
1027 * @throws \CiviCRM_API3_Exception
c364c544
MW
1028 * @dataProvider getThousandSeparators
1029 */
0b577f4b 1030 public function testCreateProportionalEntryZeroAmount(string $thousandSeparator): void {
c364c544 1031 $this->setCurrencySeparators($thousandSeparator);
0b577f4b 1032 [$contribution, $financialAccount] = $this->createContributionWithTax(['total_amount' => 0]);
9099cab3 1033 $params = [
c364c544
MW
1034 'total_amount' => 0,
1035 'to_financial_account_id' => $financialAccount->financial_account_id,
1036 'payment_instrument_id' => 1,
1037 'trxn_date' => date('Ymd'),
1038 'status_id' => 1,
1039 'entity_id' => $contribution['id'],
9099cab3 1040 ];
c364c544 1041 $financialTrxn = $this->callAPISuccess('FinancialTrxn', 'create', $params);
9099cab3 1042 $entityParams = [
c364c544
MW
1043 'contribution_total_amount' => $contribution['total_amount'],
1044 'trxn_total_amount' => 0,
1045 'line_item_amount' => 0,
9099cab3 1046 ];
c364c544 1047 $previousLineItem = CRM_Financial_BAO_FinancialItem::getPreviousFinancialItem($contribution['id']);
9099cab3 1048 $eftParams = [
c364c544
MW
1049 'entity_table' => 'civicrm_financial_item',
1050 'entity_id' => $previousLineItem['id'],
1051 'financial_trxn_id' => (string) $financialTrxn['id'],
9099cab3 1052 ];
c364c544 1053 CRM_Contribute_BAO_Contribution::createProportionalEntry($entityParams, $eftParams);
9099cab3 1054 $trxnTestArray = array_merge($eftParams, [
c364c544 1055 'amount' => '0.00',
9099cab3 1056 ]);
c364c544
MW
1057 $this->callAPISuccessGetSingle('EntityFinancialTrxn', $eftParams, $trxnTestArray);
1058 }
1059
646bc565
PN
1060 /**
1061 * Test for function getLastFinancialItemIds().
1062 */
1063 public function testgetLastFinancialItemIds() {
0b577f4b
EM
1064 [$contribution, $financialAccount] = $this->createContributionWithTax();
1065 [$ftIds, $taxItems] = CRM_Contribute_BAO_Contribution::getLastFinancialItemIds($contribution['id']);
646bc565
PN
1066 $this->assertEquals(count($ftIds), 1, 'Invalid count.');
1067 $this->assertEquals(count($taxItems), 1, 'Invalid count.');
1068 foreach ($taxItems as $value) {
1069 $this->assertEquals($value['amount'], 10, 'Invalid tax amount.');
1070 }
1071 }
1072
2a4a2f00 1073 /**
97c818fd 1074 * Test to ensure proportional entries are creating when adding a payment..
1075 *
1076 * In this test we create a pending contribution for $110 consisting of $100 contribution and $10 tax.
1077 *
1078 * We pay $50, resulting in it being allocated as $45.45 paymnt & $4.55 tax. This is in equivalent proportions
1079 * to the original payment - ie. .0909 of the $110 is 10 & that * 50 is $4.54 (note the rounding seems wrong as it should be
1080 * saved un-rounded).
2a4a2f00 1081 */
de7391fc 1082 public function testCreateProportionalFinancialEntriesViaPaymentCreate(): void {
0b577f4b 1083 [$contribution, $financialAccount] = $this->createContributionWithTax([], FALSE);
97c818fd 1084 $params = [
3137781d 1085 'total_amount' => 50,
2a4a2f00
PN
1086 'to_financial_account_id' => $financialAccount->financial_account_id,
1087 'payment_instrument_id' => 1,
1088 'trxn_date' => date('Ymd'),
1089 'status_id' => 1,
2a4a2f00 1090 'entity_id' => $contribution['id'],
97c818fd 1091 'contribution_id' => $contribution['id'],
1092 ];
1093 $financialTrxn = $this->callAPISuccess('Payment', 'create', $params);
1094 $eftParams = [
2a4a2f00
PN
1095 'entity_table' => 'civicrm_financial_item',
1096 'financial_trxn_id' => $financialTrxn['id'],
97c818fd 1097 ];
2a4a2f00 1098 $entityFinancialTrxn = $this->callAPISuccess('EntityFinancialTrxn', 'Get', $eftParams);
de7391fc 1099 $this->assertEquals(2, $entityFinancialTrxn['count'], 'Invalid count.');
97c818fd 1100 $testAmount = [4.55, 45.45];
2a4a2f00 1101 foreach ($entityFinancialTrxn['values'] as $value) {
97c818fd 1102 $this->assertEquals(array_pop($testAmount), $value['amount'], 'Invalid amount stored in civicrm_entity_financial_trxn.');
77641de4
PN
1103 }
1104 }
1105
1106 /**
1107 * Test to check if amount is proportionally asigned for PI change.
1108 */
1109 public function testProportionallyAssignedForPIChange() {
0b577f4b 1110 [$contribution, $financialAccount] = $this->createContributionWithTax();
9099cab3 1111 $params = [
77641de4
PN
1112 'id' => $contribution['id'],
1113 'payment_instrument_id' => 3,
9099cab3 1114 ];
77641de4
PN
1115 $this->callAPISuccess('Contribution', 'create', $params);
1116 $lastFinancialTrxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($contribution['id'], 'DESC');
9099cab3 1117 $eftParams = [
77641de4
PN
1118 'entity_table' => 'civicrm_financial_item',
1119 'financial_trxn_id' => $lastFinancialTrxnId['financialTrxnId'],
9099cab3 1120 ];
77641de4
PN
1121 $entityFinancialTrxn = $this->callAPISuccess('EntityFinancialTrxn', 'Get', $eftParams);
1122 $this->assertEquals($entityFinancialTrxn['count'], 2, 'Invalid count.');
9099cab3 1123 $testAmount = [10, 100];
77641de4
PN
1124 foreach ($entityFinancialTrxn['values'] as $value) {
1125 $this->assertEquals($value['amount'], array_pop($testAmount), 'Invalid amount stored in civicrm_entity_financial_trxn.');
2a4a2f00
PN
1126 }
1127 }
1128
646bc565
PN
1129 /**
1130 * Function to create contribution with tax.
1131 */
c2e77374 1132 public function createContributionWithTax($params = [], $isCompleted = TRUE): array {
c364c544
MW
1133 if (!isset($params['total_amount'])) {
1134 $params['total_amount'] = 100;
1135 }
646bc565
PN
1136 $contactId = $this->individualCreate();
1137 $this->enableTaxAndInvoicing();
1138 $financialType = $this->createFinancialType();
28d44c71 1139 $financialAccount = $this->addTaxAccountToFinancialType($financialType['id']);
c2e77374
EM
1140 /* @var CRM_Contribute_Form_Contribution $form */
1141 $form = $this->getFormObject('CRM_Contribute_Form_Contribution', [
39b959db
SL
1142 'total_amount' => $params['total_amount'],
1143 'financial_type_id' => $financialType['id'],
1144 'contact_id' => $contactId,
97c818fd 1145 'contribution_status_id' => $isCompleted ? 1 : 2,
39b959db 1146 'price_set_id' => 0,
c2e77374
EM
1147 ]);
1148 $form->buildForm();
1149 $form->postProcess();
646bc565 1150 $contribution = $this->callAPISuccessGetSingle('Contribution',
9099cab3 1151 [
646bc565 1152 'contact_id' => $contactId,
9099cab3
CW
1153 'return' => ['tax_amount', 'total_amount'],
1154 ]
646bc565 1155 );
9099cab3 1156 return [$contribution, $financialAccount];
646bc565
PN
1157 }
1158
75c07fde
JP
1159 /**
1160 * Test processOnBehalfOrganization() function.
1161 */
1162 public function testProcessOnBehalfOrganization() {
9099cab3 1163 $orgInfo = [
75c07fde
JP
1164 'phone' => '11111111',
1165 'email' => 'testorg@gmail.com',
1166 'street_address' => 'test Street',
1167 'city' => 'test City',
1168 'state_province' => 'AA',
1169 'postal_code' => '222222',
1170 'country' => 'United States',
9099cab3 1171 ];
e4addeb2 1172 $originalContactId = $contactID = $this->individualCreate();
9099cab3
CW
1173 $orgId = $this->organizationCreate(['organization_name' => 'testorg1']);
1174 $orgCount = $this->callAPISuccessGetCount('Contact', [
b9466f09
EM
1175 'contact_type' => 'Organization',
1176 'organization_name' => 'testorg1',
9099cab3 1177 ]);
75c07fde
JP
1178 $this->assertEquals($orgCount, 1);
1179
9099cab3 1180 $values = $params = [];
e4addeb2 1181 $originalBehalfOrganization = $behalfOrganization = [
75c07fde 1182 'organization_name' => 'testorg1',
9099cab3
CW
1183 'phone' => [
1184 1 => [
75c07fde
JP
1185 'phone' => $orgInfo['phone'],
1186 'is_primary' => 1,
9099cab3
CW
1187 ],
1188 ],
1189 'email' => [
1190 1 => [
75c07fde
JP
1191 'email' => $orgInfo['email'],
1192 'is_primary' => 1,
9099cab3
CW
1193 ],
1194 ],
1195 'address' => [
1196 3 => [
75c07fde
JP
1197 'street_address' => $orgInfo['street_address'],
1198 'city' => $orgInfo['city'],
1199 'location_type_id' => 3,
1200 'postal_code' => $orgInfo['postal_code'],
1201 'country' => 'US',
1202 'state_province' => 'AA',
1203 'is_primary' => 1,
9099cab3
CW
1204 ],
1205 ],
1206 ];
1207 $fields = [
75c07fde
JP
1208 'organization_name' => 1,
1209 'phone-3-1' => 1,
1210 'email-3' => 1,
1211 'street_address-3' => 1,
1212 'city-3' => 1,
1213 'postal_code-3' => 1,
1214 'country-3' => 1,
1215 'state_province-3' => 1,
9099cab3 1216 ];
e4addeb2
JG
1217 $empty = [];
1218 CRM_Contribute_Form_Contribution_Confirm::processOnBehalfOrganization($behalfOrganization, $contactID, $empty, $empty, $empty);
75c07fde 1219
e4addeb2 1220 //Check whether new organisation is created.
9099cab3 1221 $result = $this->callAPISuccess('Contact', 'get', [
b9466f09
EM
1222 'contact_type' => 'Organization',
1223 'organization_name' => 'testorg1',
9099cab3 1224 ]);
75c07fde
JP
1225 $this->assertEquals($result['count'], 1);
1226
1227 //Assert all org values are updated.
1228 foreach ($orgInfo as $key => $val) {
1229 $this->assertEquals($result['values'][$orgId][$key], $val);
1230 }
1231
1232 //Check if alert is assigned to params if more than 1 dupe exists.
9099cab3 1233 $orgId = $this->organizationCreate(['organization_name' => 'testorg1', 'email' => 'testorg@gmail.com']);
e4addeb2 1234 CRM_Contribute_Form_Contribution_Confirm::processOnBehalfOrganization($originalBehalfOrganization, $originalContactId, $values, $params, $fields);
75c07fde
JP
1235 $this->assertEquals($params['onbehalf_dupe_alert'], 1);
1236 }
1237
7e2ec997
E
1238 /**
1239 * Test for replaceContributionTokens.
bd6b5299
EM
1240 *
1241 * This function tests whether the contribution tokens are replaced with
9b3cb77d
EM
1242 * values from contribution.
1243 *
1244 * @throws \CiviCRM_API3_Exception
7e2ec997 1245 */
9b3cb77d 1246 public function testReplaceContributionTokens(): void {
9945400a 1247 $customGroup = $this->customGroupCreate(['extends' => 'Contribution', 'title' => 'contribution stuff']);
875e076b 1248 $customField = $this->customFieldOptionValueCreate($customGroup, 'myCustomField');
7e2ec997 1249 $contactId1 = $this->individualCreate();
9099cab3 1250 $params = [
7e2ec997
E
1251 'contact_id' => $contactId1,
1252 'receive_date' => '20120511',
1253 'total_amount' => 100.00,
1254 'financial_type_id' => 1,
1255 'trxn_id' => 12345,
1256 'invoice_id' => 67890,
1257 'source' => 'SSF',
1258 'contribution_status_id' => 2,
875e076b 1259 "custom_{$customField['id']}" => 'value1',
6fc6274e 1260 'currency' => 'EUR',
9099cab3 1261 ];
7e2ec997
E
1262 $contribution1 = $this->contributionCreate($params);
1263 $contactId2 = $this->individualCreate();
9099cab3 1264 $params = [
7e2ec997
E
1265 'contact_id' => $contactId2,
1266 'receive_date' => '20150511',
1267 'total_amount' => 200.00,
1268 'financial_type_id' => 1,
1269 'trxn_id' => 6789,
1270 'invoice_id' => 12345,
1271 'source' => 'ABC',
1272 'contribution_status_id' => 1,
875e076b 1273 "custom_{$customField['id']}" => 'value2',
9099cab3 1274 ];
7e2ec997 1275 $contribution2 = $this->contributionCreate($params);
9099cab3 1276 $ids = [$contribution1, $contribution2];
7e2ec997 1277
03cb51fd 1278 $subject = 'This is a test for contribution ID: {contribution.contribution_id}';
9b3cb77d 1279 $text = 'Contribution Amount: {contribution.total_amount}';
7e2ec997
E
1280 $html = "<p>Contribution Source: {contribution.contribution_source}</p></br>
1281 <p>Contribution Invoice ID: {contribution.invoice_id}</p></br>
875e076b 1282 <p>Contribution Receive Date: {contribution.receive_date}</p></br>
9b3cb77d
EM
1283 <p>Contribution Custom Field: {contribution.custom_{$customField['id']}}</p></br>
1284 {contribution.contribution_status_id:name}";
7e2ec997
E
1285
1286 $subjectToken = CRM_Utils_Token::getTokens($subject);
1287 $messageToken = CRM_Utils_Token::getTokens($text);
1288 $messageToken = array_merge($messageToken, CRM_Utils_Token::getTokens($html));
1289
1290 $contributionDetails = CRM_Contribute_BAO_Contribution::replaceContributionTokens(
1291 $ids,
1292 $subject,
1293 $subjectToken,
1294 $text,
1295 $html,
1296 $messageToken,
1297 TRUE
1298 );
1299
b9466f09 1300 $this->assertEquals('Contribution Amount: € 100.00', $contributionDetails[$contactId1]['text'], 'The text does not match');
9b3cb77d 1301 $this->assertEquals('<p>Contribution Source: ABC</p></br>
7e2ec997 1302 <p>Contribution Invoice ID: 12345</p></br>
a1d274b9 1303 <p>Contribution Receive Date: May 11th, 2015 12:00 AM</p></br>
9b3cb77d
EM
1304 <p>Contribution Custom Field: Label2</p></br>
1305 Completed', $contributionDetails[$contactId2]['html'], 'The html does not match');
7e2ec997
E
1306 }
1307
7b5169d0
PN
1308 /**
1309 * Test for contribution with deferred revenue.
1310 */
1311 public function testContributionWithDeferredRevenue() {
1312 $contactId = $this->individualCreate();
1313 Civi::settings()->set('deferred_revenue_enabled', TRUE);
9099cab3 1314 $params = [
7b5169d0
PN
1315 'contact_id' => $contactId,
1316 'receive_date' => '20120511',
1317 'total_amount' => 100.00,
1318 'financial_type_id' => 'Event Fee',
1319 'trxn_id' => 12345,
1320 'invoice_id' => 67890,
1321 'source' => 'SSF',
1322 'contribution_status_id' => 'Completed',
b9466f09 1323 'revenue_recognition_date' => date('Ymd', strtotime('+3 month')),
9099cab3 1324 ];
7b5169d0
PN
1325 $contribution = $this->callAPISuccess('contribution', 'create', $params);
1326
9099cab3 1327 $this->callAPISuccessGetCount('EntityFinancialTrxn', [
b9466f09 1328 'entity_table' => 'civicrm_contribution',
7b5169d0 1329 'entity_id' => $contribution['id'],
9099cab3 1330 ], 2);
7b5169d0 1331
9099cab3 1332 $checkAgainst = [
7b5169d0
PN
1333 'financial_trxn_id.to_financial_account_id.name' => 'Deferred Revenue - Event Fee',
1334 'financial_trxn_id.from_financial_account_id.name' => 'Event Fee',
f818aed5 1335 'financial_trxn_id' => '2',
9099cab3
CW
1336 ];
1337 $result = $this->callAPISuccessGetSingle('EntityFinancialTrxn', [
1338 'return' => [
b9466f09
EM
1339 'financial_trxn_id.from_financial_account_id.name',
1340 'financial_trxn_id.to_financial_account_id.name',
1341 'financial_trxn_id',
9099cab3 1342 ],
b9466f09 1343 'entity_table' => 'civicrm_contribution',
7b5169d0
PN
1344 'entity_id' => $contribution['id'],
1345 'financial_trxn_id.is_payment' => 0,
9099cab3 1346 ], $checkAgainst);
7b5169d0 1347
9099cab3 1348 $result = $this->callAPISuccessGetSingle('EntityFinancialTrxn', [
b9466f09 1349 'entity_table' => 'civicrm_financial_item',
7b5169d0 1350 'financial_trxn_id' => $result['financial_trxn_id'],
9099cab3
CW
1351 'return' => ['entity_id'],
1352 ]);
7b5169d0 1353
9099cab3 1354 $checkAgainst = [
7b5169d0
PN
1355 'financial_account_id.name' => 'Deferred Revenue - Event Fee',
1356 'id' => $result['entity_id'],
9099cab3
CW
1357 ];
1358 $result = $this->callAPISuccessGetSingle('FinancialItem', [
7b5169d0 1359 'id' => $result['entity_id'],
b9466f09 1360 'return' => ['financial_account_id.name'],
9099cab3 1361 ], $checkAgainst);
7b5169d0
PN
1362 }
1363
a495d83a
PN
1364 /**
1365 * https://lab.civicrm.org/dev/financial/issues/56
1366 * Changing financial type on a contribution records correct financial items
1367 */
1368 public function testChangingFinancialTypeWithoutTax() {
1369 $ids = $values = [];
1370 $contactId = $this->individualCreate();
9099cab3 1371 $params = [
a495d83a
PN
1372 'contact_id' => $contactId,
1373 'receive_date' => date('YmdHis'),
1374 'total_amount' => 100.00,
1375 'financial_type_id' => 'Donation',
1376 'contribution_status_id' => 'Completed',
9099cab3 1377 ];
a495d83a
PN
1378 /* first test the scenario when sending an email */
1379 $contributionId = $this->callAPISuccess(
1380 'contribution',
1381 'create',
1382 $params
1383 )['id'];
1384
1385 // Update Financial Type.
1386 $this->callAPISuccess('contribution', 'create', [
1387 'id' => $contributionId,
1388 'financial_type_id' => 'Event Fee',
1389 ]);
1390
1391 // Get line item
1392 $lineItem = $this->callAPISuccessGetSingle('LineItem', [
1393 'contribution_id' => $contributionId,
b9466f09 1394 'return' => ['financial_type_id.name', 'line_total'],
a495d83a
PN
1395 ]);
1396
1397 $this->assertEquals(
1398 $lineItem['line_total'],
1399 100.00,
1400 'Invalid line amount.'
1401 );
1402
1403 $this->assertEquals(
1404 $lineItem['financial_type_id.name'],
1405 'Event Fee',
1406 'Invalid Financial Type stored.'
1407 );
1408
1409 // Get Financial Items.
1410 $financialItems = $this->callAPISuccess('FinancialItem', 'get', [
1411 'entity_id' => $lineItem['id'],
1412 'sequential' => 1,
1413 'entity_table' => 'civicrm_line_item',
b9466f09
EM
1414 'options' => ['sort' => 'id'],
1415 'return' => ['financial_account_id.name', 'amount', 'description'],
a495d83a
PN
1416 ]);
1417
1418 $this->assertEquals($financialItems['count'], 3, 'Count mismatch.');
1419
1420 $toCheck = [
1421 ['Donation', 100.00],
1422 ['Donation', -100.00],
1423 ['Event Fee', 100.00],
1424 ];
1425
1426 foreach ($financialItems['values'] as $key => $values) {
1427 $this->assertEquals(
1428 $values['financial_account_id.name'],
1429 $toCheck[$key][0],
1430 'Invalid Financial Account stored.'
1431 );
1432 $this->assertEquals(
1433 $values['amount'],
1434 $toCheck[$key][1],
1435 'Amount mismatch.'
1436 );
1437 $this->assertEquals(
1438 $values['description'],
1439 'Contribution Amount',
1440 'Description mismatch.'
1441 );
1442 }
1443
1444 // Check transactions.
1445 $financialTransactions = $this->callAPISuccess('EntityFinancialTrxn', 'get', [
b9466f09
EM
1446 'return' => ['financial_trxn_id'],
1447 'entity_table' => 'civicrm_contribution',
a495d83a
PN
1448 'entity_id' => $contributionId,
1449 'sequential' => 1,
1450 ]);
1451 $this->assertEquals($financialTransactions['count'], 3, 'Count mismatch.');
1452
1453 foreach ($financialTransactions['values'] as $key => $values) {
1454 $this->callAPISuccessGetCount('EntityFinancialTrxn', [
1455 'financial_trxn_id' => $values['financial_trxn_id'],
1456 'amount' => $toCheck[$key][1],
1457 'financial_trxn_id.total_amount' => $toCheck[$key][1],
1458 ], 2);
1459 }
1460 }
1461
1db3ddea
KE
1462 /**
1463 * CRM-21424 Check if the receipt update is set after composing the receipt message
1464 */
1465 public function testSendMailUpdateReceiptDate() {
9099cab3 1466 $ids = $values = [];
1db3ddea 1467 $contactId = $this->individualCreate();
9099cab3 1468 $params = [
1db3ddea
KE
1469 'contact_id' => $contactId,
1470 'receive_date' => '20120511',
1471 'total_amount' => 100.00,
1472 'financial_type_id' => 'Donation',
1473 'source' => 'SSF',
1474 'contribution_status_id' => 'Completed',
9099cab3 1475 ];
1db3ddea
KE
1476 /* first test the scenario when sending an email */
1477 $contribution = $this->callAPISuccess('contribution', 'create', $params);
1478 $contributionId = $contribution['id'];
1479 $this->assertDBNull('CRM_Contribute_BAO_Contribution', $contributionId, 'receipt_date', 'id', 'After creating receipt date must be null');
9099cab3 1480 $input = ['receipt_update' => 0];
1db3ddea
KE
1481 CRM_Contribute_BAO_Contribution::sendMail($input, $ids, $contributionId, $values);
1482 $this->assertDBNull('CRM_Contribute_BAO_Contribution', $contributionId, 'receipt_date', 'id', 'After sendMail, with the explicit instruction not to update receipt date stays null');
9099cab3 1483 $input = ['receipt_update' => 1];
1db3ddea
KE
1484 CRM_Contribute_BAO_Contribution::sendMail($input, $ids, $contributionId, $values);
1485 $this->assertDBNotNull('CRM_Contribute_BAO_Contribution', $contributionId, 'receipt_date', 'id', 'After sendMail with the permission to allow update receipt date must be set');
1486
1487 /* repeat the same scenario for downloading a pdf */
1488 $contribution = $this->callAPISuccess('contribution', 'create', $params);
1489 $contributionId = $contribution['id'];
1490 $this->assertDBNull('CRM_Contribute_BAO_Contribution', $contributionId, 'receipt_date', 'id', 'After creating receipt date must be null');
9099cab3 1491 $input = ['receipt_update' => 0];
1db3ddea
KE
1492 /* setting the lasast parameter (returnmessagetext) to TRUE is done by the download of the pdf */
1493 CRM_Contribute_BAO_Contribution::sendMail($input, $ids, $contributionId, $values, TRUE);
1494 $this->assertDBNull('CRM_Contribute_BAO_Contribution', $contributionId, 'receipt_date', 'id', 'After sendMail, with the explicit instruction not to update receipt date stays null');
9099cab3 1495 $input = ['receipt_update' => 1];
1db3ddea
KE
1496 CRM_Contribute_BAO_Contribution::sendMail($input, $ids, $contributionId, $values, TRUE);
1497 $this->assertDBNotNull('CRM_Contribute_BAO_Contribution', $contributionId, 'receipt_date', 'id', 'After sendMail with the permission to allow update receipt date must be set');
1498 }
1499
e26d0d27 1500 /**
1501 * Test cancel order api when a pledge is linked.
1502 *
1503 * The pledge status should be updated. I believe the contribution should
1504 * also be unlinked but the goal at this point is no change.
1505 *
1506 * @throws CRM_Core_Exception
1507 * @throws \CiviCRM_API3_Exception
1508 * @throws \API_Exception
1509 */
1510 public function testCancelOrderWithPledge(): void {
1511 $this->ids['contact'][0] = $this->individualCreate();
1512 $pledgeID = (int) $this->callAPISuccess('Pledge', 'create', ['contact_id' => $this->ids['contact'][0], 'amount' => 4, 'installments' => 2, 'frequency_unit' => 'month', 'original_installment_amount' => 2, 'create_date' => 'now', 'financial_type_id' => 'Donation', 'start_date' => '+5 days'])['id'];
1513 $orderID = (int) $this->callAPISuccess('Order', 'create', ['contact_id' => $this->ids['contact'][0], 'total_amount' => 2, 'financial_type_id' => 'Donation', 'api.Payment.create' => ['total_amount' => 2]])['id'];
1514 $pledgePayments = $this->callAPISuccess('PledgePayment', 'get')['values'];
1515 $this->callAPISuccess('PledgePayment', 'create', ['id' => key($pledgePayments), 'pledge_id' => $pledgeID, 'contribution_id' => $orderID, 'status_id' => 'Completed', 'actual_amount' => 2]);
1516 $beforePledge = $this->callAPISuccessGetSingle('Pledge', ['id' => $pledgeID]);
1517 $this->assertEquals(2, $beforePledge['pledge_total_paid']);
1518 $this->callAPISuccess('Order', 'cancel', ['contribution_id' => $orderID]);
1519
1520 $this->callAPISuccessGetSingle('Contribution', ['contribution_status_id' => 'Cancelled']);
1521 $afterPledge = $this->callAPISuccessGetSingle('Pledge', ['id' => $pledgeID]);
1522 $this->assertEquals('', $afterPledge['pledge_total_paid']);
1523 $payments = PledgePayment::get(FALSE)->addWhere('contribution_id', 'IS NOT NULL')->execute();
1524 $this->assertCount(0, $payments);
1525 }
1526
df43ca01
PN
1527 /**
1528 * Test contribution update when more than one quick
1529 * config line item is linked to contribution.
1530 *
1531 * @throws CRM_Core_Exception
1532 * @throws \CiviCRM_API3_Exception
1533 * @throws \API_Exception
1534 */
1535 public function testContributionQuickConfigTwoLineItems(): void {
1536 $contactId1 = $this->individualCreate();
1537 $contactId2 = $this->individualCreate();
1538 $membershipOrganizationId = $this->organizationCreate();
1539
1540 // Created new contribution to bypass the deprecated error
1541 // 'Per https://lab.civicrm.org/dev/core/issues/15 this data fix should not be required.'
1542 // in CRM_Price_BAO_LineItem::processPriceSet();
1543 $this->callAPISuccess('Contribution', 'create', [
1544 'contact_id' => $contactId1,
1545 'receive_date' => '2010-01-20',
1546 'financial_type_id' => 'Member Dues',
1547 'contribution_status_id' => 'Completed',
1548 'total_amount' => 150,
1549 ]);
1550 $this->callAPISuccess('Contribution', 'create', [
1551 'contact_id' => $contactId1,
1552 'receive_date' => '2010-01-20',
1553 'financial_type_id' => 'Member Dues',
1554 'contribution_status_id' => 'Completed',
1555 'total_amount' => 150,
1556 ]);
1557
1558 // create membership type
1559 $membershipTypeId1 = $this->callAPISuccess('MembershipType', 'create', [
1560 'domain_id' => 1,
1561 'member_of_contact_id' => $membershipOrganizationId,
1562 'financial_type_id' => 'Member Dues',
1563 'duration_unit' => 'month',
1564 'duration_interval' => 1,
1565 'period_type' => 'rolling',
1566 'minimum_fee' => 100,
1567 'name' => 'Parent',
1568 ])['id'];
1569
1570 $membershipTypeId2 = $this->callAPISuccess('MembershipType', 'create', [
1571 'domain_id' => 1,
1572 'member_of_contact_id' => $membershipOrganizationId,
1573 'financial_type_id' => 'Member Dues',
1574 'duration_unit' => 'month',
1575 'duration_interval' => 1,
1576 'period_type' => 'rolling',
1577 'minimum_fee' => 50,
1578 'name' => 'Child',
1579 ])['id'];
1580
1581 $contactIds = [
1582 $contactId1 => $membershipTypeId1,
1583 $contactId2 => $membershipTypeId2,
1584 ];
1585
1586 $priceFields = CRM_Price_BAO_PriceSet::getDefaultPriceSet('membership');
1587
1588 // prepare order api params.
1589 $p = [
1590 'contact_id' => $contactId1,
1591 'receive_date' => '2010-01-20',
1592 'financial_type_id' => 'Member Dues',
1593 'contribution_status_id' => 'Pending',
1594 'total_amount' => 150,
1595 'api.Payment.create' => ['total_amount' => 150],
1596 ];
1597
1598 $now = date('Ymd');
1599 foreach ($priceFields as $priceField) {
1600 $lineItems = [];
1601 $contactId = array_search($priceField['membership_type_id'], $contactIds);
1602 $lineItems[1] = [
1603 'price_field_id' => $priceField['priceFieldID'],
1604 'price_field_value_id' => $priceField['priceFieldValueID'],
1605 'label' => $priceField['label'],
1606 'field_title' => $priceField['label'],
1607 'qty' => 1,
1608 'unit_price' => $priceField['amount'],
1609 'line_total' => $priceField['amount'],
1610 'financial_type_id' => $priceField['financial_type_id'],
1611 'entity_table' => 'civicrm_membership',
1612 'membership_type_id' => $priceField['membership_type_id'],
1613 ];
1614 $p['line_items'][] = [
1615 'line_item' => $lineItems,
1616 'params' => [
1617 'contact_id' => $contactId,
1618 'membership_type_id' => $priceField['membership_type_id'],
1619 'source' => 'Payment',
1620 'join_date' => '2020-04-28',
1621 'start_date' => '2020-04-28',
1622 'status_id' => 'Pending',
1623 'is_override' => 1,
1624 ],
1625 ];
1626 }
1627 $order = $this->callAPISuccess('order', 'create', $p);
1628 $contributionId = $order['id'];
1629
1630 $count = CRM_Core_DAO::singleValueQuery('
1631 SELECT count(*), total_amount
1632 FROM civicrm_contribution cc
1633 INNER JOIN civicrm_line_item cli
1634 ON cli.contribution_id = cc.id
1635 AND cc.id = %1
1636 GROUP BY cc.id, total_amount
1637 HAVING SUM(cli.line_total) != total_amount
1638 ', [1 => [$contributionId, 'Integer']]);
1639
1640 $this->assertEquals(0, $count);
1641
1642 $this->callAPISuccess('Contribution', 'create', [
1643 'id' => $contributionId,
1644 'total_amount' => 150,
1645 ]);
1646 $count = CRM_Core_DAO::singleValueQuery('
1647 SELECT count(*), total_amount
1648 FROM civicrm_contribution cc
1649 INNER JOIN civicrm_line_item cli
1650 ON cli.contribution_id = cc.id
1651 AND cc.id = %1
1652 GROUP BY cc.id, total_amount
1653 HAVING SUM(cli.line_total) != total_amount
1654 ', [1 => [$contributionId, 'Integer']]);
1655
1656 $this->assertEquals(0, $count);
1657 }
1658
6b5e6603
MF
1659 /**
1660 * Test activity contact is updated when contribution contact is changed
1661 */
1662 public function testUpdateActivityContactOnContributionContactChange(): void {
1663 $contactId_1 = $this->individualCreate();
1664 $contactId_2 = $this->individualCreate();
1665 $contactId_3 = $this->individualCreate();
1666
1667 $contributionParams = [
1668 'financial_type_id' => 'Donation',
1669 'receive_date' => date('Y-m-d H:i:s'),
1670 'sequential' => TRUE,
1671 'total_amount' => 50,
1672 ];
1673
1674 // Case 1: Only source contact, no target contact
1675
1676 $contribution = $this->callAPISuccess('Contribution', 'create', array_merge(
1677 $contributionParams,
1678 ['contact_id' => $contactId_1]
1679 ))['values'][0];
1680
1681 $activity = $this->callAPISuccessGetSingle('Activity', ['source_record_id' => $contribution['id']]);
1682
1683 $activityContactParams = [
1684 'activity_id' => $activity['id'],
1685 'record_type_id' => 'Activity Source',
1686 ];
1687
1688 $activityContact = $this->callAPISuccessGetSingle('ActivityContact', $activityContactParams);
1689
1690 $this->assertEquals($activityContact['contact_id'], $contactId_1, 'Check source contact ID matches the first contact');
1691
1692 $contribution = $this->callAPISuccess('Contribution', 'create', array_merge(
1693 $contributionParams,
1694 [
1695 'id' => $contribution['id'],
1696 'contact_id' => $contactId_2,
1697 ]
1698 ))['values'][0];
1699
1700 $activityContact = $this->callAPISuccessGetSingle('ActivityContact', $activityContactParams);
1701
1702 $this->assertEquals($activityContact['contact_id'], $contactId_2, 'Check source contact ID matches the second contact');
1703
1704 // Case 2: Source and target contact
1705
1706 $contribution = $this->callAPISuccess('Contribution', 'create', array_merge(
1707 $contributionParams,
1708 [
1709 'contact_id' => $contactId_1,
1710 'source_contact_id' => $contactId_3,
1711 ]
1712 ))['values'][0];
1713
1714 $activity = $this->callAPISuccessGetSingle('Activity', ['source_record_id' => $contribution['id']]);
1715
1716 $activityContactParams = [
1717 'activity_id' => $activity['id'],
1718 'record_type_id' => 'Activity Targets',
1719 ];
1720
1721 $activityContact = $this->callAPISuccessGetSingle('ActivityContact', $activityContactParams);
1722
1723 $this->assertEquals($activityContact['contact_id'], $contactId_1, 'Check target contact ID matches first contact');
1724
1725 $contribution = $this->callAPISuccess('Contribution', 'create', array_merge(
1726 $contributionParams,
1727 [
1728 'id' => $contribution['id'],
1729 'contact_id' => $contactId_2,
1730 ]
1731 ))['values'][0];
1732
1733 $activityContact = $this->callAPISuccessGetSingle('ActivityContact', $activityContactParams);
1734
1735 $this->assertEquals($activityContact['contact_id'], $contactId_2, 'Check target contact ID matches the second contact');
1736 }
1737
b89a5f6f
PF
1738 /**
1739 * Test status updates triggering activity creation and value propagation
1740 *
1741 * @throws \API_Exception
1742 * @throws \Civi\API\Exception\UnauthorizedException
1743 */
1744 public function testContributionStatusUpdateActivityPropagation() {
1745 $contactId = $this->individualCreate();
1746 $campaignId = $this->campaignCreate();
1747 $contribution = Contribution::create()
1748 ->addValue('contact_id', $contactId)
1749 ->addValue('campaign_id', $campaignId)
1750 ->addValue('financial_type_id:name', 'Donation')
1751 ->addValue('total_amount', 50)
1752 ->addValue('contribution_status_id:name', 'Pending')
1753 ->execute()
1754 ->first();
1755 $activityWhere = [
1756 ['source_record_id', '=', $contribution['id']],
1757 ['activity_type_id:name', '=', 'Contribution'],
1758 ];
1759 $activity = Activity::get()->setWhere($activityWhere)->execute()->first();
1760 $this->assertNull($activity, 'Should not create contribution activity for pending contribution');
1761
1762 Contribution::update()
1763 ->addWhere('id', '=', $contribution['id'])
1764 ->addValue('contribution_status_id:name', 'Completed')
1765 ->execute();
1766
1767 $activity = Activity::get()->setWhere($activityWhere)->execute()->first();
1768 $this->assertEquals($campaignId, $activity['campaign_id'], 'Should have created contribution activity with campaign');
1769
1770 $newCampaignId = $this->campaignCreate();
1771 Contribution::update()
1772 ->addWhere('id', '=', $contribution['id'])
1773 ->addValue('campaign_id', $newCampaignId)
1774 ->execute();
1775
1776 $activity = Activity::get()->setWhere($activityWhere)->execute()->first();
1777 $this->assertEquals($newCampaignId, $activity['campaign_id'], 'Should have updated contribution activity to new campaign');
1778
1779 Contribution::update()
1780 ->addWhere('id', '=', $contribution['id'])
1781 ->addValue('campaign_id', NULL)
1782 ->execute();
1783
1784 $activity = Activity::get()->setWhere($activityWhere)->execute()->first();
1785 $this->assertNull($activity['campaign_id'], 'Should have removed campaign from contribution activity');
1786 }
1787
6a488035 1788}