Test regression fix
[civicrm-core.git] / tests / phpunit / CRM / Contribute / Form / ContributionTest.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 +--------------------------------------------------------------------+
10 */
11
12 use Civi\Api4\PriceSet;
13
14 /**
15 * Test APIv3 civicrm_contribute_* functions
16 *
17 * @package CiviCRM_APIv3
18 * @subpackage API_Contribution
19 * @group headless
20 */
21 class CRM_Contribute_Form_ContributionTest extends CiviUnitTestCase {
22 use CRMTraits_PCP_PCPTestTrait;
23
24 protected $_individualId;
25 protected $_contribution;
26 protected $_financialTypeId = 1;
27 protected $_entity = 'Contribution';
28 protected $_params;
29 protected $_ids = [];
30 protected $_pageParams = [];
31 protected $_userId;
32
33 /**
34 * Parameters to create payment processor.
35 *
36 * @var array
37 */
38 protected $_processorParams = [];
39
40 /**
41 * ID of created event.
42 *
43 * @var int
44 */
45 protected $_eventID;
46
47 /**
48 * Payment instrument mapping.
49 *
50 * @var array
51 */
52 protected $paymentInstruments = [];
53
54 /**
55 * Products.
56 *
57 * @var array
58 */
59 protected $products = [];
60
61 /**
62 * Dummy payment processor.
63 *
64 * @var CRM_Core_Payment_Dummy
65 */
66 protected $paymentProcessor;
67
68 /**
69 * Payment processor ID.
70 *
71 * @var int
72 */
73 protected $paymentProcessorID;
74
75 /**
76 * Setup function.
77 *
78 * @throws \CRM_Core_Exception
79 * @throws \CiviCRM_API3_Exception
80 */
81 public function setUp(): void {
82 $this->_apiversion = 3;
83 parent::setUp();
84 $this->_userId = $this->createLoggedInUser();
85
86 $this->_individualId = $this->ids['contact'][0] = $this->individualCreate();
87 $this->_params = [
88 'contact_id' => $this->_individualId,
89 'receive_date' => '20120511',
90 'total_amount' => 100.00,
91 'financial_type_id' => $this->_financialTypeId,
92 'non_deductible_amount' => 10.00,
93 'fee_amount' => 5.00,
94 'net_amount' => 95.00,
95 'source' => 'SSF',
96 'contribution_status_id' => 1,
97 ];
98 $this->_processorParams = [
99 'domain_id' => 1,
100 'name' => 'Dummy',
101 'payment_processor_type_id' => 10,
102 'financial_account_id' => 12,
103 'is_active' => 1,
104 'user_name' => '',
105 'url_site' => 'http://dummy.com',
106 'url_recur' => 'http://dummy.com',
107 'billing_mode' => 1,
108 ];
109
110 $instruments = $this->callAPISuccess('contribution', 'getoptions', ['field' => 'payment_instrument_id']);
111 $this->paymentInstruments = $instruments['values'];
112 $product1 = $this->callAPISuccess('product', 'create', [
113 'name' => 'Smurf',
114 'options' => 'brainy smurf, clumsy smurf, papa smurf',
115 ]);
116
117 $this->products[] = $product1['values'][$product1['id']];
118 $this->paymentProcessor = $this->dummyProcessorCreate();
119 $processor = $this->paymentProcessor->getPaymentProcessor();
120 $this->paymentProcessorID = $processor['id'];
121 }
122
123 /**
124 * Clean up after each test.
125 *
126 * @throws \CRM_Core_Exception
127 */
128 public function tearDown(): void {
129 $this->quickCleanUpFinancialEntities();
130 $this->quickCleanup(['civicrm_note', 'civicrm_uf_match', 'civicrm_address']);
131 }
132
133 /**
134 * CHeck that all tests that have created payments have created them with the right financial entities.
135 *
136 * @throws \CRM_Core_Exception
137 */
138 protected function assertPostConditions(): void {
139 $this->validateAllPayments();
140 }
141
142 /**
143 * Test the submit function on the contribution page.
144 *
145 * @param string $thousandSeparator
146 *
147 * @dataProvider getThousandSeparators
148 */
149 public function testSubmit(string $thousandSeparator): void {
150 $this->setCurrencySeparators($thousandSeparator);
151 $form = $this->getContributionForm([
152 'total_amount' => $this->formatMoneyInput(1234),
153 'financial_type_id' => 1,
154 'contact_id' => $this->_individualId,
155 'payment_instrument_id' => $this->getPaymentInstrument('Check'),
156 'contribution_status_id' => 1,
157 ]);
158 $form->postProcess();
159 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId]);
160 $this->assertEmpty($contribution['amount_level']);
161 $this->assertEquals(1234, $contribution['total_amount']);
162 $this->assertEquals(1234, $contribution['net_amount']);
163 }
164
165 /**
166 * Test the submit function on the contribution page.
167 *
168 * @throws \CRM_Core_Exception
169 * @throws \CiviCRM_API3_Exception
170 */
171 public function testSubmitCreditCard(): void {
172 $form = new CRM_Contribute_Form_Contribution();
173 $form->testSubmit([
174 'total_amount' => 50,
175 'financial_type_id' => 1,
176 'contact_id' => $this->_individualId,
177 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments),
178 'contribution_status_id' => 1,
179 ], CRM_Core_Action::ADD);
180 $this->callAPISuccessGetCount('Contribution', [
181 'contact_id' => $this->_individualId,
182 'contribution_status_id' => 'Completed',
183 ], 1);
184 }
185
186 /**
187 * Test the submit function on the contribution page.
188 *
189 * @throws \CRM_Core_Exception
190 * @throws \CiviCRM_API3_Exception
191 */
192 public function testSubmitCreditCardPayPal(): void {
193 $mut = new CiviMailUtils($this, TRUE);
194 $mut->clearMessages();
195 $form = new CRM_Contribute_Form_Contribution();
196 $paymentProcessorID = $this->paymentProcessorCreate(['is_test' => 0]);
197 $form->_mode = 'Live';
198 $error = FALSE;
199 try {
200 $form->testSubmit([
201 'total_amount' => 50,
202 'financial_type_id' => 1,
203 'contact_id' => $this->_individualId,
204 'contribution_status_id' => 1,
205 'credit_card_number' => 4444333322221111,
206 'cvv2' => 123,
207 'credit_card_exp_date' => [
208 'M' => 9,
209 'Y' => 2025,
210 ],
211 'credit_card_type' => 'Visa',
212 'billing_first_name' => 'Junko',
213 'billing_middle_name' => '',
214 'billing_last_name' => 'Adams',
215 'billing_street_address-5' => '790L Lincoln St S',
216 'billing_city-5' => 'Maryknoll',
217 'billing_state_province_id-5' => 1031,
218 'billing_postal_code-5' => 10545,
219 'billing_country_id-5' => 1228,
220 'frequency_interval' => 1,
221 'frequency_unit' => 'month',
222 'installments' => '',
223 'hidden_AdditionalDetail' => 1,
224 'hidden_Premium' => 1,
225 'from_email_address' => '"civi45" <civi45@civicrm.com>',
226 'is_email_receipt' => TRUE,
227 'receipt_date' => '',
228 'receipt_date_time' => '',
229 'payment_processor_id' => $paymentProcessorID,
230 'currency' => 'USD',
231 'source' => 'bob sled race',
232 ], CRM_Core_Action::ADD);
233 }
234 catch (Civi\Payment\Exception\PaymentProcessorException $e) {
235 $error = TRUE;
236 }
237
238 $contribution = $this->callAPISuccess('Contribution', 'get', [
239 'contact_id' => $this->_individualId,
240 'contribution_status_id' => $error ? 'Failed' : 'Completed',
241 'payment_instrument_id' => $this->callAPISuccessGetValue('PaymentProcessor', [
242 'return' => 'payment_instrument_id',
243 'id' => $paymentProcessorID,
244 ]),
245 ]);
246
247 $this->assertEquals(1, $contribution['count'], 'Contribution count should be one.');
248 $this->assertNotTrue(empty($contribution['values'][$contribution['id']]['receipt_date']), 'Receipt date should not be blank.');
249
250 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $this->_individualId]);
251 $this->assertTrue(empty($contact['source']));
252 if (!$error) {
253 $msgs = $mut->getAllMessages();
254 $this->assertEquals(1, count($msgs));
255 }
256 $mut->clearMessages();
257 $mut->stop();
258 }
259
260 /**
261 * Test the submit function on the contribution page
262 *
263 * @throws \CRM_Core_Exception
264 */
265 public function testSubmitCreditCardWithEmailReceipt() {
266 $mut = new CiviMailUtils($this, TRUE);
267 $mut->clearMessages();
268 $form = new CRM_Contribute_Form_Contribution();
269 $form->_mode = 'Live';
270
271 $form->testSubmit([
272 'total_amount' => 50,
273 'financial_type_id' => 1,
274 'contact_id' => $this->_individualId,
275 'contribution_status_id' => 1,
276 'credit_card_number' => 4444333322221111,
277 'cvv2' => 123,
278 'credit_card_exp_date' => [
279 'M' => 9,
280 'Y' => 2025,
281 ],
282 'credit_card_type' => 'Visa',
283 'billing_first_name' => 'Junko',
284 'billing_middle_name' => '',
285 'billing_last_name' => 'Adams',
286 'billing_street_address-5' => '790L Lincoln St S',
287 'billing_city-5' => 'Maryknoll',
288 'billing_state_province_id-5' => 1031,
289 'billing_postal_code-5' => 10545,
290 'billing_country_id-5' => 1228,
291 'frequency_interval' => 1,
292 'frequency_unit' => 'month',
293 'installments' => '',
294 'hidden_AdditionalDetail' => 1,
295 'hidden_Premium' => 1,
296 'from_email_address' => '"civi45" <civi45@civicrm.com>',
297 'is_email_receipt' => TRUE,
298 'receipt_date' => '',
299 'receipt_date_time' => '',
300 'payment_processor_id' => $this->paymentProcessorID,
301 'currency' => 'USD',
302 'source' => 'bob sled race',
303 ], CRM_Core_Action::ADD);
304
305 $this->callAPISuccessGetCount('Contribution', [
306 'contact_id' => $this->_individualId,
307 'contribution_status_id' => 'Completed',
308 'payment_instrument_id' => $this->callAPISuccessGetValue('PaymentProcessor', [
309 'return' => 'payment_instrument_id',
310 'id' => $this->paymentProcessorID,
311 ]),
312 ], 1);
313 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $this->_individualId]);
314 $this->assertTrue(empty($contact['source']));
315 $msgs = $mut->getAllMessages();
316 $this->assertEquals(1, count($msgs));
317 $mut->stop();
318 }
319
320 /**
321 * Test the submit function on the contribution page.
322 */
323 public function testSubmitCreditCardNoReceipt() {
324 $mut = new CiviMailUtils($this, TRUE);
325 $mut->clearMessages();
326 $form = new CRM_Contribute_Form_Contribution();
327 $form->_mode = 'Live';
328 $error = FALSE;
329 try {
330 $form->testSubmit([
331 'total_amount' => 60,
332 'financial_type_id' => 1,
333 'contact_id' => $this->_individualId,
334 'contribution_status_id' => 1,
335 'credit_card_number' => 4444333322221111,
336 'cvv2' => 123,
337 'credit_card_exp_date' => [
338 'M' => 9,
339 'Y' => 2025,
340 ],
341 'credit_card_type' => 'Visa',
342 'billing_first_name' => 'Junko',
343 'billing_middle_name' => '',
344 'billing_last_name' => 'Adams',
345 'billing_street_address-5' => '790L Lincoln St S',
346 'billing_city-5' => 'Maryknoll',
347 'billing_state_province_id-5' => 1031,
348 'billing_postal_code-5' => 10545,
349 'billing_country_id-5' => 1228,
350 'frequency_interval' => 1,
351 'frequency_unit' => 'month',
352 'installments' => '',
353 'hidden_AdditionalDetail' => 1,
354 'hidden_Premium' => 1,
355 'from_email_address' => '"civi45" <civi45@civicrm.com>',
356 'is_email_receipt' => FALSE,
357 'receipt_date' => '',
358 'receipt_date_time' => '',
359 'payment_processor_id' => $this->paymentProcessorID,
360 'currency' => 'USD',
361 'source' => 'bob sled race',
362 ], CRM_Core_Action::ADD);
363 }
364 catch (Civi\Payment\Exception\PaymentProcessorException $e) {
365 $error = TRUE;
366 }
367
368 $this->callAPISuccessGetCount('Contribution', [
369 'contact_id' => $this->_individualId,
370 'contribution_status_id' => $error ? 'Failed' : 'Completed',
371 'payment_instrument_id' => $this->callAPISuccessGetValue('PaymentProcessor', [
372 'return' => 'payment_instrument_id',
373 'id' => $this->paymentProcessorID,
374 ]),
375 ], 1);
376 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $this->_individualId]);
377 $this->assertTrue(empty($contact['source']));
378 $mut->assertMailLogEmpty();
379 $mut->stop();
380 }
381
382 /**
383 * Test the submit function on the contribution page.
384 */
385 public function testSubmitCreditCardFee() {
386 $form = new CRM_Contribute_Form_Contribution();
387 $this->paymentProcessor->setDoDirectPaymentResult(['payment_status_id' => 1, 'is_error' => 0, 'trxn_id' => 'tx', 'fee_amount' => .08]);
388 $form->_mode = 'Live';
389 $form->testSubmit([
390 'total_amount' => 50,
391 'financial_type_id' => 1,
392 'contact_id' => $this->_individualId,
393 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments),
394 'contribution_status_id' => 1,
395 'credit_card_number' => 4444333322221111,
396 'cvv2' => 123,
397 'credit_card_exp_date' => [
398 'M' => 9,
399 'Y' => 2025,
400 ],
401 'credit_card_type' => 'Visa',
402 'billing_first_name' => 'Junko',
403 'billing_middle_name' => '',
404 'billing_last_name' => 'Adams',
405 'billing_street_address-5' => '790L Lincoln St S',
406 'billing_city-5' => 'Maryknoll',
407 'billing_state_province_id-5' => 1031,
408 'billing_postal_code-5' => 10545,
409 'billing_country_id-5' => 1228,
410 'frequency_interval' => 1,
411 'frequency_unit' => 'month',
412 'installments' => '',
413 'hidden_AdditionalDetail' => 1,
414 'hidden_Premium' => 1,
415 'from_email_address' => '"civi45" <civi45@civicrm.com>',
416 'receipt_date' => '',
417 'receipt_date_time' => '',
418 'payment_processor_id' => $this->paymentProcessorID,
419 'currency' => 'USD',
420 'source' => '',
421 ], CRM_Core_Action::ADD);
422
423 $contribution = $this->callAPISuccessGetSingle('Contribution', [
424 'contact_id' => $this->_individualId,
425 'contribution_status_id' => 'Completed',
426 ]);
427 $this->assertEquals('50.00', $contribution['total_amount']);
428 $this->assertEquals(.08, $contribution['fee_amount']);
429 $this->assertEquals(49.92, $contribution['net_amount']);
430 $this->assertEquals('tx', $contribution['trxn_id']);
431 $this->assertEmpty($contribution['amount_level']);
432 }
433
434 /**
435 * Test a fully deductible contribution submitted by credit card (CRM-16669).
436 */
437 public function testSubmitCreditCardFullyDeductible() {
438 $form = new CRM_Contribute_Form_Contribution();
439 $form->_mode = 'Live';
440 $form->testSubmit([
441 'total_amount' => 50,
442 'financial_type_id' => 1,
443 'contact_id' => $this->_individualId,
444 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments),
445 'contribution_status_id' => 1,
446 'credit_card_number' => 4444333322221111,
447 'cvv2' => 123,
448 'credit_card_exp_date' => [
449 'M' => 9,
450 'Y' => 2025,
451 ],
452 'credit_card_type' => 'Visa',
453 'billing_first_name' => 'Junko',
454 'billing_middle_name' => '',
455 'billing_last_name' => 'Adams',
456 'billing_street_address-5' => '790L Lincoln St S',
457 'billing_city-5' => 'Maryknoll',
458 'billing_state_province_id-5' => 1031,
459 'billing_postal_code-5' => 10545,
460 'billing_country_id-5' => 1228,
461 'frequency_interval' => 1,
462 'frequency_unit' => 'month',
463 'installments' => '',
464 'hidden_AdditionalDetail' => 1,
465 'hidden_Premium' => 1,
466 'from_email_address' => '"civi45" <civi45@civicrm.com>',
467 'receipt_date' => '',
468 'receipt_date_time' => '',
469 'payment_processor_id' => $this->paymentProcessorID,
470 'currency' => 'USD',
471 'source' => '',
472 ], CRM_Core_Action::ADD);
473
474 $contribution = $this->callAPISuccessGetSingle('Contribution', [
475 'contact_id' => $this->_individualId,
476 'contribution_status_id' => 'Completed',
477 ]);
478 $this->assertEquals('50.00', $contribution['total_amount']);
479 $this->assertEquals(0, $contribution['non_deductible_amount']);
480 }
481
482 /**
483 * Test the submit function with an invalid payment.
484 *
485 * We expect the contribution to be created but left pending. The payment has failed.
486 *
487 * Test covers CRM-16417 change to keep failed transactions.
488 *
489 * We are left with
490 * - 1 Contribution with status = Pending
491 * - 1 Line item
492 * - 1 civicrm_financial_item. This is linked to the line item and has a status of 3
493 *
494 * @throws \CRM_Core_Exception
495 * @throws \CiviCRM_API3_Exception
496 */
497 public function testSubmitCreditCardInvalid() {
498 $form = new CRM_Contribute_Form_Contribution();
499 $this->paymentProcessor->setDoDirectPaymentResult(['is_error' => 1]);
500 try {
501 $form->testSubmit([
502 'total_amount' => 50,
503 'financial_type_id' => 1,
504 'contact_id' => $this->_individualId,
505 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments),
506 'payment_processor_id' => $this->paymentProcessorID,
507 'credit_card_exp_date' => ['M' => 5, 'Y' => 2012],
508 'credit_card_number' => '411111111111111',
509 ], CRM_Core_Action::ADD, 'live');
510 }
511 catch (\Civi\Payment\Exception\PaymentProcessorException $e) {
512 $this->callAPISuccessGetCount('Contribution', [
513 'contact_id' => $this->_individualId,
514 'contribution_status_id' => 'Failed',
515 ], 1);
516 $lineItem = $this->callAPISuccessGetSingle('line_item', []);
517 $this->assertEquals('50.00', $lineItem['unit_price']);
518 $this->assertEquals('50.00', $lineItem['line_total']);
519 $this->assertEquals(1, $lineItem['qty']);
520 $this->assertEquals(1, $lineItem['financial_type_id']);
521 $financialItem = $this->callAPISuccessGetSingle('financial_item', [
522 'civicrm_line_item' => $lineItem['id'],
523 'entity_id' => $lineItem['id'],
524 ]);
525 $this->assertEquals('50.00', $financialItem['amount']);
526 $this->assertEquals(3, $financialItem['status_id']);
527 return;
528 }
529 $this->fail('An expected exception has not been raised.');
530 }
531
532 /**
533 * Test the submit function creates a billing address if provided.
534 *
535 * @throws \CRM_Core_Exception
536 * @throws \CiviCRM_API3_Exception
537 */
538 public function testSubmitCreditCardWithBillingAddress() {
539 $form = new CRM_Contribute_Form_Contribution();
540 $form->testSubmit([
541 'total_amount' => 50,
542 'financial_type_id' => 1,
543 'contact_id' => $this->_individualId,
544 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments),
545 'payment_processor_id' => $this->paymentProcessorID,
546 'credit_card_exp_date' => ['M' => 5, 'Y' => 2025],
547 'credit_card_number' => '411111111111111',
548 'billing_city-5' => 'Vancouver',
549 ], CRM_Core_Action::ADD, 'live');
550 $contribution = $this->callAPISuccessGetSingle('Contribution', ['return' => 'address_id']);
551 $this->assertNotEmpty($contribution['address_id']);
552 // CRM-18490 : There is a unwanted test leakage due to below getsingle Api as it only fails in Jenkin
553 // for now we are only fetching address on based on Address ID (removed filter location_type_id and city)
554 $this->callAPISuccessGetSingle('Address', [
555 'id' => $contribution['address_id'],
556 ]);
557 }
558
559 /**
560 * CRM-20745: Test the submit function correctly sets the
561 * receive date for recurring contribution.
562 */
563 public function testSubmitCreditCardWithRecur() {
564 $form = new CRM_Contribute_Form_Contribution();
565 $receiveDate = date('Y-m-d H:i:s', strtotime('+1 month'));
566 $form->testSubmit([
567 'total_amount' => 50,
568 'financial_type_id' => 1,
569 'is_recur' => 1,
570 'frequency_interval' => 2,
571 'frequency_unit' => 'month',
572 'installments' => 2,
573 'receive_date' => $receiveDate,
574 'contact_id' => $this->_individualId,
575 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments),
576 'payment_processor_id' => $this->paymentProcessorID,
577 'credit_card_exp_date' => ['M' => 5, 'Y' => 2025],
578 'credit_card_number' => '411111111111111',
579 'billing_city-5' => 'Vancouver',
580 ], CRM_Core_Action::ADD, 'live');
581 $contribution = $this->callAPISuccessGetSingle('Contribution', ['return' => 'receive_date']);
582 $this->assertEquals($contribution['receive_date'], $receiveDate);
583 }
584
585 /**
586 * Test the submit function does not create a billing address if no details provided.
587 */
588 public function testSubmitCreditCardWithNoBillingAddress() {
589 $form = new CRM_Contribute_Form_Contribution();
590 $form->testSubmit([
591 'total_amount' => 50,
592 'financial_type_id' => 1,
593 'contact_id' => $this->_individualId,
594 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments),
595 'payment_processor_id' => $this->paymentProcessorID,
596 'credit_card_exp_date' => ['M' => 5, 'Y' => 2025],
597 'credit_card_number' => '411111111111111',
598 ], CRM_Core_Action::ADD, 'live');
599 $contribution = $this->callAPISuccessGetSingle('Contribution', ['return' => 'address_id']);
600 $this->assertEmpty($contribution['address_id']);
601 $this->callAPISuccessGetCount('Address', [
602 'city' => 'Vancouver',
603 'location_type_id' => 5,
604 ], 0);
605 }
606
607 /**
608 * Test the submit function on the contribution page.
609 *
610 * @throws \CRM_Core_Exception
611 * @throws \CiviCRM_API3_Exception
612 */
613 public function testSubmitEmailReceipt() {
614 $form = new CRM_Contribute_Form_Contribution();
615 $mut = new CiviMailUtils($this, TRUE);
616 $form->testSubmit([
617 'total_amount' => 50,
618 'financial_type_id' => 1,
619 'contact_id' => $this->_individualId,
620 'is_email_receipt' => TRUE,
621 'from_email_address' => 'test@test.com',
622 'contribution_status_id' => 1,
623 ], CRM_Core_Action::ADD);
624 $this->callAPISuccessGetCount('Contribution', ['contact_id' => $this->_individualId], 1);
625 $mut->checkMailLog([
626 'Contribution Information',
627 ]);
628 $mut->stop();
629 }
630
631 /**
632 * Test the submit function on the contribution page using numerical from email address.
633 */
634 public function testSubmitEmailReceiptUserEmailFromAddress(): void {
635 $email = $this->callAPISuccess('Email', 'create', [
636 'contact_id' => $this->_userId,
637 'email' => 'testLoggedIn@example.com',
638 ]);
639 $mut = new CiviMailUtils($this, TRUE);
640 $form = $this->getContributionForm([
641 'contribution_status_id' => 1,
642 'total_amount' => 50,
643 'financial_type_id' => 1,
644 'contact_id' => $this->_individualId,
645 'is_email_receipt' => TRUE,
646 'from_email_address' => $email['id'],
647 ]);
648
649 $form->postProcess();
650 $this->callAPISuccessGetCount('Contribution', ['contact_id' => $this->_individualId], 1);
651 $mut->checkMailLog([
652 'Below you will find a receipt for this contribution.',
653 '<testloggedin@example.com>',
654 ]);
655 $mut->stop();
656 }
657
658 /**
659 * Ensure that price field are shown during pay later/pending Contribution
660 *
661 * @throws \API_Exception
662 */
663 public function testEmailReceiptOnPayLater(): void {
664 $donationFT = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialType', 'Donation', 'id', 'name');
665 $priceSetID = PriceSet::create(FALSE)->setValues([
666 'title' => 'Price Set abcd',
667 'is_active' => TRUE,
668 'financial_type_id:name' => 'Donation',
669 'extends' => 2,
670 'name' => 'price_set_abcd',
671 ])->execute()->first()['id'];
672
673 $paramsField = [
674 'label' => 'Price Field',
675 'name' => 'price_field',
676 'html_type' => 'CheckBox',
677 'option_label' => ['1' => 'Price Field 1', '2' => 'Price Field 2'],
678 'option_value' => ['1' => 100, '2' => 200],
679 'option_name' => ['1' => 'Price Field 1', '2' => 'Price Field 2'],
680 'option_weight' => ['1' => 1, '2' => 2],
681 'option_amount' => ['1' => 100, '2' => 200],
682 'is_display_amounts' => 1,
683 'weight' => 1,
684 'options_per_line' => 1,
685 'is_active' => ['1' => 1, '2' => 1],
686 'price_set_id' => $priceSetID,
687 'is_enter_qty' => 1,
688 'financial_type_id' => $donationFT,
689 ];
690 $priceField = CRM_Price_BAO_PriceField::create($paramsField);
691 $priceFieldValue = $this->callAPISuccess('PriceFieldValue', 'get', ['price_field_id' => $priceField->id]);
692
693 $params = [
694 'total_amount' => 100,
695 'financial_type_id' => $donationFT,
696 'contact_id' => $this->_individualId,
697 'is_email_receipt' => TRUE,
698 'from_email_address' => 'test@test.com',
699 'price_set_id' => $priceSetID,
700 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'),
701 ];
702
703 foreach ($priceFieldValue['values'] as $id => $price) {
704 if ($price['amount'] == 100) {
705 $params['price_' . $priceField->id] = [$id => 1];
706 }
707 }
708 $form = $this->getContributionForm($params);
709 $mut = new CiviMailUtils($this, TRUE);
710 $form->_priceSet = current(CRM_Price_BAO_PriceSet::getSetDetail($priceSetID));
711 $form->postProcess();
712
713 $mut->checkMailLog([
714 'Financial Type: Donation
715 ---------------------------------------------------------
716 Item Qty Each Total
717 ----------------------------------------------------------
718 Price Field - Price Field 1 1 $ 100.00 $ 100.00
719 ',
720 ]);
721 $mut->stop();
722 }
723
724 /**
725 * Test that a contribution is assigned against a pledge.
726 */
727 public function testUpdatePledge() {
728 $pledge = $this->callAPISuccess('pledge', 'create', [
729 'contact_id' => $this->_individualId,
730 'pledge_create_date' => date('Ymd'),
731 'start_date' => date('Ymd'),
732 'amount' => 100.00,
733 'pledge_status_id' => '2',
734 'pledge_financial_type_id' => '1',
735 'pledge_original_installment_amount' => 20,
736 'frequency_interval' => 5,
737 'frequency_unit' => 'year',
738 'frequency_day' => 15,
739 'installments' => 2,
740 'sequential' => 1,
741 ]);
742 $pledgePaymentID = $this->callAPISuccess('pledge_payment', 'getvalue', [
743 'pledge_id' => $pledge['id'],
744 'options' => ['limit' => 1],
745 'return' => 'id',
746 ]);
747 $form = new CRM_Contribute_Form_Contribution();
748 $form->testSubmit([
749 'total_amount' => 50,
750 'financial_type_id' => 1,
751 'contact_id' => $this->_individualId,
752 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
753 'pledge_payment_id' => $pledgePaymentID,
754 'contribution_status_id' => 1,
755 ], CRM_Core_Action::ADD);
756 $pledgePayment = $this->callAPISuccess('pledge_payment', 'getsingle', ['id' => $pledgePaymentID]);
757 $this->assertNotEmpty($pledgePayment['contribution_id']);
758 $this->assertEquals($pledgePayment['actual_amount'], 50);
759 $this->assertEquals(1, $pledgePayment['status_id']);
760 }
761
762 /**
763 * Test functions involving premiums.
764 */
765 public function testPremiumUpdate() {
766 $form = new CRM_Contribute_Form_Contribution();
767 $mut = new CiviMailUtils($this, TRUE);
768 $form->testSubmit([
769 'total_amount' => 50,
770 'financial_type_id' => 1,
771 'contact_id' => $this->_individualId,
772 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
773 'contribution_status_id' => 1,
774 'product_name' => [$this->products[0]['id'], 1],
775 'fulfilled_date' => '',
776 'is_email_receipt' => TRUE,
777 'from_email_address' => 'test@test.com',
778 ], CRM_Core_Action::ADD);
779 $contributionProduct = $this->callAPISuccess('contribution_product', 'getsingle', []);
780 $this->assertEquals('clumsy smurf', $contributionProduct['product_option']);
781 $mut->checkMailLog([
782 'Premium Information',
783 'Smurf',
784 'clumsy smurf',
785 ]);
786 $mut->stop();
787 }
788
789 /**
790 * Test functions involving premiums.
791 */
792 public function testPremiumUpdateCreditCard() {
793 $form = new CRM_Contribute_Form_Contribution();
794 $mut = new CiviMailUtils($this, TRUE);
795 $form->testSubmit([
796 'total_amount' => 50,
797 'financial_type_id' => 1,
798 'contact_id' => $this->_individualId,
799 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
800 'contribution_status_id' => 1,
801 'product_name' => [$this->products[0]['id'], 1],
802 'fulfilled_date' => '',
803 'is_email_receipt' => TRUE,
804 'from_email_address' => 'test@test.com',
805 'payment_processor_id' => $this->paymentProcessorID,
806 'credit_card_exp_date' => ['M' => 5, 'Y' => 2026],
807 'credit_card_number' => '411111111111111',
808 ], CRM_Core_Action::ADD, 'live');
809 $contributionProduct = $this->callAPISuccess('contribution_product', 'getsingle', []);
810 $this->assertEquals('clumsy smurf', $contributionProduct['product_option']);
811 $mut->checkMailLog([
812 'Premium Information',
813 'Smurf',
814 'clumsy smurf',
815 ]);
816 $mut->stop();
817 }
818
819 /**
820 * Test submitting the back office contribution form with pcp data.
821 *
822 * @throws \CRM_Core_Exception
823 * @throws \CiviCRM_API3_Exception
824 * @throws \Civi\Payment\Exception\PaymentProcessorException
825 */
826 public function testSubmitWithPCP(): void {
827 $mut = new CiviMailUtils($this, TRUE);
828 $mut->clearMessages();
829 $params = $this->pcpParams();
830 $pcpID = $this->createPCPBlock($params);
831 $form = new CRM_Contribute_Form_Contribution();
832 $form->testSubmit([
833 'financial_type_id' => 3,
834 'contact_id' => $this->_individualId,
835 'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
836 'contribution_status_id' => 1,
837 'total_amount' => 5,
838 'pcp_made_through_id' => $pcpID,
839 'pcp_display_in_roll' => '1',
840 'pcp_roll_nickname' => 'Dobby',
841 'pcp_personal_note' => 'I wuz here',
842 ], CRM_Core_Action::ADD);
843 $softCredit = $this->callAPISuccessGetSingle('ContributionSoft', []);
844 $this->assertEquals('Dobby', $softCredit['pcp_roll_nickname']);
845 $mut->checkMailLog(['Personal Campaign Page Owner Notification']);
846 }
847
848 /**
849 * Test the submit function on the contribution page.
850 */
851 public function testSubmitWithNote() {
852 $form = new CRM_Contribute_Form_Contribution();
853 $form->testSubmit([
854 'total_amount' => 50,
855 'financial_type_id' => 1,
856 'contact_id' => $this->_individualId,
857 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
858 'contribution_status_id' => 1,
859 'note' => 'Super cool and interesting stuff',
860 ], CRM_Core_Action::ADD);
861 $this->callAPISuccessGetCount('Contribution', ['contact_id' => $this->_individualId], 1);
862 $note = $this->callAPISuccessGetSingle('note', ['entity_table' => 'civicrm_contribution']);
863 $this->assertEquals($note['note'], 'Super cool and interesting stuff');
864 }
865
866 /**
867 * Test the submit function on the contribution page.
868 */
869 public function testSubmitWithNoteCreditCard() {
870 $form = new CRM_Contribute_Form_Contribution();
871
872 $form->testSubmit([
873 'total_amount' => 50,
874 'financial_type_id' => 1,
875 'contact_id' => $this->_individualId,
876 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
877 'contribution_status_id' => 1,
878 'note' => 'Super cool and interesting stuff',
879 ] + $this->getCreditCardParams(),
880 CRM_Core_Action::ADD);
881 $this->callAPISuccessGetCount('Contribution', ['contact_id' => $this->_individualId], 1);
882 $note = $this->callAPISuccessGetSingle('note', ['entity_table' => 'civicrm_contribution']);
883 $this->assertEquals($note['note'], 'Super cool and interesting stuff');
884 }
885
886 /**
887 * Test that if a negative contribution is entered it does not get reset to $0.
888 *
889 * Note that this fails locally for me & I believe there may be an issue for some sites
890 * with negative numbers. Grep for CRM-16460 to find the places I think that might
891 * be affected if you hit this.
892 */
893 public function testEnterNegativeContribution() {
894 $form = new CRM_Contribute_Form_Contribution();
895 $form->testSubmit([
896 'total_amount' => -5,
897 'financial_type_id' => 1,
898 'contact_id' => $this->_individualId,
899 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
900 'contribution_status_id' => 1,
901 ],
902 CRM_Core_Action::ADD);
903 $this->callAPISuccessGetCount('Contribution', ['contact_id' => $this->_individualId], 1);
904
905 $contribution = $this->callAPISuccessGetSingle('Contribution', [
906 'contact_id' => $this->_individualId,
907 ]);
908 $this->assertEquals(-5, $contribution['total_amount']);
909 }
910
911 /**
912 * Test the submit function on the contribution page.
913 *
914 * @param string $thousandSeparator
915 *
916 * @throws \CRM_Core_Exception
917 * @throws \CiviCRM_API3_Exception
918 * @throws \Civi\Payment\Exception\PaymentProcessorException
919 *
920 * @dataProvider getThousandSeparators
921 */
922 public function testSubmitUpdate(string $thousandSeparator): void {
923 $this->setCurrencySeparators($thousandSeparator);
924 $form = new CRM_Contribute_Form_Contribution();
925
926 $form->testSubmit([
927 'total_amount' => $this->formatMoneyInput(6100.10),
928 'financial_type_id' => 1,
929 'contact_id' => $this->_individualId,
930 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
931 'contribution_status_id' => 1,
932 'price_set_id' => 0,
933 ], CRM_Core_Action::ADD);
934 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId]);
935 $form->testSubmit([
936 'total_amount' => $this->formatMoneyInput(5200.20),
937 'net_amount' => $this->formatMoneyInput(5200.20),
938 'financial_type_id' => 1,
939 'contact_id' => $this->_individualId,
940 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
941 'contribution_status_id' => 1,
942 'price_set_id' => 0,
943 'id' => $contribution['id'],
944 ], CRM_Core_Action::UPDATE);
945 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId]);
946 $this->assertEquals(5200.20, $contribution['total_amount'], 2);
947
948 $financialTransactions = $this->callAPISuccess('FinancialTrxn', 'get', ['sequential' => TRUE]);
949 $this->assertEquals(2, $financialTransactions['count']);
950 $this->assertEquals(6100.10, $financialTransactions['values'][0]['total_amount']);
951 $this->assertEquals(-899.90, $financialTransactions['values'][1]['total_amount']);
952 $this->assertEquals(-899.90, $financialTransactions['values'][1]['net_amount']);
953 $lineItem = $this->callAPISuccessGetSingle('LineItem', []);
954 $this->assertEquals(5200.20, $lineItem['line_total']);
955 }
956
957 /**
958 * Test the submit function if only payment instrument is changed from 'Check' to 'Credit Card'
959 *
960 * @param string $thousandSeparator
961 *
962 * @dataProvider getThousandSeparators
963 */
964 public function testSubmitUpdateChangePaymentInstrument($thousandSeparator) {
965 $this->setCurrencySeparators($thousandSeparator);
966 $form = new CRM_Contribute_Form_Contribution();
967
968 $form->testSubmit([
969 'total_amount' => 1200.55,
970 'financial_type_id' => 1,
971 'contact_id' => $this->_individualId,
972 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
973 'check_number' => '123AX',
974 'contribution_status_id' => 1,
975 'price_set_id' => 0,
976 ], CRM_Core_Action::ADD);
977 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId]);
978 $form->testSubmit([
979 'total_amount' => 1200.55,
980 'net_amount' => 1200.55,
981 'financial_type_id' => 1,
982 'contact_id' => $this->_individualId,
983 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments),
984 'card_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Financial_DAO_FinancialTrxn', 'card_type_id', 'Visa'),
985 'pan_truncation' => '1011',
986 'contribution_status_id' => 1,
987 'price_set_id' => 0,
988 'id' => $contribution['id'],
989 ], CRM_Core_Action::UPDATE);
990 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId]);
991 $this->assertEquals(1200.55, $contribution['total_amount']);
992
993 $financialTransactions = $this->callAPISuccess('FinancialTrxn', 'get', ['sequential' => TRUE]);
994 $this->assertEquals(3, $financialTransactions['count']);
995
996 [$oldTrxn, $reversedTrxn, $latestTrxn] = $financialTransactions['values'];
997
998 $this->assertEquals(1200.55, $oldTrxn['total_amount']);
999 $this->assertEquals('123AX', $oldTrxn['check_number']);
1000 $this->assertEquals(array_search('Check', $this->paymentInstruments), $oldTrxn['payment_instrument_id']);
1001
1002 $this->assertEquals(-1200.55, $reversedTrxn['total_amount']);
1003 $this->assertEquals('123AX', $reversedTrxn['check_number']);
1004 $this->assertEquals(array_search('Check', $this->paymentInstruments), $reversedTrxn['payment_instrument_id']);
1005
1006 $this->assertEquals(1200.55, $latestTrxn['total_amount']);
1007 $this->assertEquals('1011', $latestTrxn['pan_truncation']);
1008 $this->assertEquals(array_search('Credit Card', $this->paymentInstruments), $latestTrxn['payment_instrument_id']);
1009 $lineItem = $this->callAPISuccessGetSingle('LineItem', []);
1010 }
1011
1012 /**
1013 * Get parameters for credit card submit calls.
1014 *
1015 * @return array
1016 * Credit card specific parameters.
1017 */
1018 protected function getCreditCardParams() {
1019 return [
1020 'payment_processor_id' => $this->paymentProcessorID,
1021 'credit_card_exp_date' => ['M' => 5, 'Y' => 2012],
1022 'credit_card_number' => '411111111111111',
1023 ];
1024 }
1025
1026 /**
1027 * Test the submit function that completes the partially paid payment using Credit Card
1028 *
1029 * @throws \CRM_Core_Exception
1030 * @throws \CiviCRM_API3_Exception
1031 */
1032 public function testPartialPaymentWithCreditCard(): void {
1033 // create a partially paid contribution by using back-office form
1034 $form = $this->getContributionForm([
1035 'total_amount' => 50,
1036 'financial_type_id' => 1,
1037 'contact_id' => $this->_individualId,
1038 'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
1039 'check_number' => '7890',
1040 'billing_city-5' => 'Vancouver',
1041 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'),
1042 ]);
1043 $form->postProcess();
1044
1045 $contribution = $this->callAPISuccessGetSingle('Contribution', []);
1046 $this->callAPISuccess('Payment', 'create', ['contribution_id' => $contribution['id'], 'total_amount' => 10, 'payment_instrument_id' => 'Cash']);
1047 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $contribution['id']]);
1048 $this->assertEquals('Partially paid', $contribution['contribution_status']);
1049 // pay additional amount by using Credit Card
1050 $form = new CRM_Contribute_Form_AdditionalPayment();
1051 $form->testSubmit([
1052 'contribution_id' => $contribution['id'],
1053 'contact_id' => $this->_individualId,
1054 'total_amount' => 40,
1055 'currency' => 'USD',
1056 'financial_type_id' => 1,
1057 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments),
1058 'payment_processor_id' => $this->paymentProcessorID,
1059 'credit_card_exp_date' => ['M' => 5, 'Y' => 2025],
1060 'credit_card_number' => '411111111111111',
1061 'cvv2' => 234,
1062 'credit_card_type' => 'Visa',
1063 'billing_city-5' => 'Vancouver',
1064 'billing_state_province_id-5' => 1059,
1065 'billing_postal_code-5' => 1321312,
1066 'billing_country_id-5' => 1228,
1067 'trxn_date' => '2017-04-11 13:05:11',
1068 ], 'live');
1069 $contribution = $this->callAPISuccessGetSingle('Contribution', []);
1070 $this->assertNotEmpty($contribution);
1071 $this->assertEquals('Completed', $contribution['contribution_status']);
1072 }
1073
1074 /**
1075 * Test the submit function for FT with tax.
1076 *
1077 * @param string $thousandSeparator
1078 *
1079 * @dataProvider getThousandSeparators
1080 */
1081 public function testSubmitSaleTax($thousandSeparator) {
1082 $this->setCurrencySeparators($thousandSeparator);
1083 $this->enableTaxAndInvoicing();
1084 $this->addTaxAccountToFinancialType($this->_financialTypeId);
1085 $form = new CRM_Contribute_Form_Contribution();
1086
1087 $form->testSubmit([
1088 'total_amount' => $this->formatMoneyInput(1000.00),
1089 'financial_type_id' => $this->_financialTypeId,
1090 'contact_id' => $this->_individualId,
1091 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
1092 'contribution_status_id' => 1,
1093 'price_set_id' => 0,
1094 ],
1095 CRM_Core_Action::ADD
1096 );
1097 $contribution = $this->callAPISuccessGetSingle('Contribution',
1098 [
1099 'contact_id' => $this->_individualId,
1100 'return' => ['tax_amount', 'total_amount'],
1101 ]
1102 );
1103 $this->assertEquals(1100, $contribution['total_amount']);
1104 $this->assertEquals(100, $contribution['tax_amount']);
1105 $this->callAPISuccessGetCount('FinancialTrxn', [], 1);
1106 $this->callAPISuccessGetCount('FinancialItem', [], 2);
1107 $lineItem = $this->callAPISuccessGetSingle('LineItem', ['contribution_id' => $contribution['id']]);
1108 $this->assertEquals(1000, $lineItem['line_total']);
1109 $this->assertEquals(100, $lineItem['tax_amount']);
1110
1111 // CRM-20423: Upon simple submit of 'Edit Contribution' form ensure that total amount is same
1112 $form->testSubmit([
1113 'id' => $contribution['id'],
1114 'financial_type_id' => 3,
1115 'contact_id' => $this->_individualId,
1116 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
1117 'contribution_status_id' => 1,
1118 ], CRM_Core_Action::UPDATE);
1119
1120 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId]);
1121 // Check if total amount is unchanged
1122 $this->assertEquals(1100, $contribution['total_amount']);
1123 }
1124
1125 /**
1126 * Test the submit function for FT without tax.
1127 *
1128 * @throws \CRM_Core_Exception
1129 * @throws \CiviCRM_API3_Exception
1130 * @throws \Civi\Payment\Exception\PaymentProcessorException
1131 */
1132 public function testSubmitWithOutSaleTax() {
1133 $this->enableTaxAndInvoicing();
1134 $this->addTaxAccountToFinancialType($this->_financialTypeId);
1135 $form = new CRM_Contribute_Form_Contribution();
1136
1137 $form->testSubmit([
1138 'total_amount' => 100,
1139 'financial_type_id' => 3,
1140 'contact_id' => $this->_individualId,
1141 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
1142 'contribution_status_id' => 1,
1143 'price_set_id' => 0,
1144 ], CRM_Core_Action::ADD);
1145 $contribution = $this->callAPISuccessGetSingle('Contribution',
1146 [
1147 'contact_id' => $this->_individualId,
1148 'return' => ['tax_amount', 'total_amount'],
1149 ]
1150 );
1151 $this->assertEquals(100, $contribution['total_amount']);
1152 $this->assertEquals(0, (float) $contribution['tax_amount']);
1153 $this->callAPISuccessGetCount('FinancialTrxn', [], 1);
1154 $this->callAPISuccessGetCount('FinancialItem', [], 1);
1155 $lineItem = $this->callAPISuccessGetSingle('LineItem', [
1156 'contribution_id' => $contribution['id'],
1157 'return' => ['line_total', 'tax_amount'],
1158 ]);
1159 $this->assertEquals(100, $lineItem['line_total']);
1160 $this->assertEquals(0.00, $lineItem['tax_amount']);
1161 }
1162
1163 /**
1164 * Create a contribution & then edit it via backoffice form, checking tax with: default price_set
1165 *
1166 * @param string $thousandSeparator
1167 *
1168 * @dataProvider getThousandSeparators
1169 *
1170 * @throws \Exception
1171 */
1172 public function testReSubmitSaleTax($thousandSeparator): void {
1173 $this->setCurrencySeparators($thousandSeparator);
1174 $this->enableTaxAndInvoicing();
1175 $this->addTaxAccountToFinancialType($this->_financialTypeId);
1176 [$form, $contribution] = $this->doInitialSubmit();
1177 $this->assertEquals(11000, $contribution['total_amount']);
1178 $this->assertEquals(1000, $contribution['tax_amount']);
1179 $this->assertEquals(11000, $contribution['net_amount']);
1180
1181 $mut = new CiviMailUtils($this, TRUE);
1182 // Testing here if when we edit something trivial like adding a check_number tax, net, total amount stay the same:
1183 $form->testSubmit([
1184 'id' => $contribution['id'],
1185 'tax_amount' => $contribution['tax_amount'],
1186 'financial_type_id' => $contribution['financial_type_id'],
1187 'receive_date' => $contribution['receive_date'],
1188 'payment_instrument_id' => $contribution['payment_instrument_id'],
1189 'price_set_id' => 0,
1190 'check_number' => 12345,
1191 'contribution_status_id' => 1,
1192 'is_email_receipt' => 1,
1193 'from_email_address' => 'demo@example.com',
1194 ], CRM_Core_Action::UPDATE);
1195 $contribution = $this->callAPISuccessGetSingle('Contribution',
1196 [
1197 'contribution_id' => 1,
1198 'return' => ['tax_amount', 'total_amount', 'net_amount', 'financial_type_id', 'receive_date', 'payment_instrument_id'],
1199 ]
1200 );
1201 $this->assertEquals(11000, $contribution['total_amount']);
1202 $this->assertEquals(1000, $contribution['tax_amount']);
1203 $this->assertEquals(11000, $contribution['net_amount']);
1204
1205 $strings = [
1206 'Total Tax Amount : $ ' . $this->formatMoneyInput(1000.00),
1207 'Total Amount : $ ' . $this->formatMoneyInput(11000.00),
1208 'Date Received: April 21st, 2015',
1209 'Paid By: Check',
1210 'Check Number: 12345',
1211 ];
1212
1213 $mut->checkMailLog($strings);
1214 $this->callAPISuccessGetCount('FinancialTrxn', [], 3);
1215 $items = $this->callAPISuccess('FinancialItem', 'get', ['sequential' => 1])['values'];
1216 $this->assertCount(2, $items);
1217 $this->assertEquals('Contribution Amount', $items[0]['description']);
1218 $this->assertEquals('Sales Tax', $items[1]['description']);
1219
1220 $this->assertEquals(10000, $items[0]['amount']);
1221 $this->assertEquals(1000, $items[1]['amount']);
1222 }
1223
1224 /**
1225 * Create a contribution & then edit it via backoffice form, checking tax with: default price_set
1226 *
1227 * @param string $thousandSeparator
1228 *
1229 * @dataProvider getThousandSeparators
1230 *
1231 * @throws \Exception
1232 */
1233 public function testReSubmitSaleTaxAlteredAmount($thousandSeparator) {
1234 $this->setCurrencySeparators($thousandSeparator);
1235 $this->enableTaxAndInvoicing();
1236 $this->addTaxAccountToFinancialType($this->_financialTypeId);
1237 [$form, $contribution] = $this->doInitialSubmit();
1238
1239 $mut = new CiviMailUtils($this, TRUE);
1240 // Testing here if when we edit something trivial like adding a check_number tax, net, total amount stay the same:
1241 $form->testSubmit([
1242 'id' => $contribution['id'],
1243 'total_amount' => $this->formatMoneyInput(20000),
1244 'tax_amount' => $this->formatMoneyInput(2000),
1245 'financial_type_id' => $contribution['financial_type_id'],
1246 'receive_date' => $contribution['receive_date'],
1247 'payment_instrument_id' => $contribution['payment_instrument_id'],
1248 'price_set_id' => 0,
1249 'check_number' => 12345,
1250 'contribution_status_id' => 1,
1251 'is_email_receipt' => 1,
1252 'from_email_address' => 'demo@example.com',
1253 ], CRM_Core_Action::UPDATE);
1254 $contribution = $this->callAPISuccessGetSingle('Contribution',
1255 [
1256 'contribution_id' => 1,
1257 'return' => ['tax_amount', 'total_amount', 'net_amount', 'financial_type_id', 'receive_date', 'payment_instrument_id'],
1258 ]
1259 );
1260 $this->assertEquals(22000, $contribution['total_amount']);
1261 $this->assertEquals(2000, $contribution['tax_amount']);
1262 $this->assertEquals(22000, $contribution['net_amount']);
1263
1264 $strings = [
1265 'Total Tax Amount : $ ' . $this->formatMoneyInput(2000),
1266 'Total Amount : $ ' . $this->formatMoneyInput(22000.00),
1267 'Date Received: April 21st, 2015',
1268 'Paid By: Check',
1269 'Check Number: 12345',
1270 ];
1271
1272 $mut->checkMailLog($strings);
1273 $this->callAPISuccessGetCount('FinancialTrxn', [], 4);
1274 $items = $this->callAPISuccess('FinancialItem', 'get', ['sequential' => 1]);
1275 $this->assertEquals(4, $items['count']);
1276 $this->assertEquals('Contribution Amount', $items['values'][0]['description']);
1277 $this->assertEquals('Sales Tax', $items['values'][1]['description']);
1278 $this->assertEquals('Contribution Amount', $items['values'][0]['description']);
1279 $this->assertEquals('Sales Tax', $items['values'][1]['description']);
1280
1281 $this->assertEquals(10000, $items['values'][0]['amount']);
1282 $this->assertEquals(1000, $items['values'][1]['amount']);
1283 $this->assertEquals(10000, $items['values'][2]['amount']);
1284 $this->assertEquals(1000, $items['values'][3]['amount']);
1285 }
1286
1287 /**
1288 * Do the first contributions, in preparation for an edit-submit.
1289 *
1290 * @return array
1291 *
1292 * @throws \Exception
1293 */
1294 protected function doInitialSubmit() {
1295 $form = new CRM_Contribute_Form_Contribution();
1296
1297 $form->testSubmit([
1298 'total_amount' => $this->formatMoneyInput(10000),
1299 'financial_type_id' => $this->_financialTypeId,
1300 'receive_date' => '2015-04-21 00:00:00',
1301 'contact_id' => $this->_individualId,
1302 'payment_instrument_id' => array_search('Check', $this->paymentInstruments),
1303 'contribution_status_id' => 1,
1304 'price_set_id' => 0,
1305 ], CRM_Core_Action::ADD);
1306 $contribution = $this->callAPISuccessGetSingle('Contribution',
1307 [
1308 'contribution_id' => 1,
1309 'return' => [
1310 'tax_amount',
1311 'total_amount',
1312 'net_amount',
1313 'financial_type_id',
1314 'receive_date',
1315 'payment_instrument_id',
1316 ],
1317 ]
1318 );
1319 $this->assertEquals(11000, $contribution['total_amount']);
1320 $this->assertEquals(1000, $contribution['tax_amount']);
1321 $this->assertEquals(11000, $contribution['net_amount']);
1322 return [$form, $contribution];
1323 }
1324
1325 /**
1326 * function to test card_type and pan truncation.
1327 */
1328 public function testCardTypeAndPanTruncation(): void {
1329 $form = $this->getContributionForm([
1330 'total_amount' => 100,
1331 'financial_type_id' => 3,
1332 'contact_id' => $this->_individualId,
1333 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments),
1334 'contribution_status_id' => 1,
1335 'credit_card_type' => 'Visa',
1336 'pan_truncation' => 4567,
1337 'price_set_id' => 0,
1338 ]);
1339 $form->postProcess();
1340 $contribution = $this->callAPISuccessGetSingle('Contribution',
1341 [
1342 'contact_id' => $this->_individualId,
1343 'return' => ['id'],
1344 ]
1345 );
1346 $lastFinancialTrxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($contribution['id'], 'DESC');
1347 $financialTrxn = $this->callAPISuccessGetSingle(
1348 'FinancialTrxn',
1349 [
1350 'id' => $lastFinancialTrxnId['financialTrxnId'],
1351 'return' => ['card_type_id.label', 'pan_truncation'],
1352 ]
1353 );
1354 $this->assertEquals('Visa', $financialTrxn['card_type_id.label']);
1355 $this->assertEquals(4567, $financialTrxn['pan_truncation']);
1356 }
1357
1358 /**
1359 * Check payment processor is correctly assigned for a contribution page.
1360 *
1361 * @throws \CRM_Core_Exception
1362 * @throws \CRM_Contribute_Exception_InactiveContributionPageException
1363 */
1364 public function testContributionBasePreProcess(): void {
1365 //Create contribution page with only pay later enabled.
1366 $params = [
1367 'title' => 'Test Contribution Page',
1368 'financial_type_id' => 1,
1369 'currency' => 'NZD',
1370 'goal_amount' => 100,
1371 'is_pay_later' => 1,
1372 'pay_later_text' => 'Send check',
1373 'is_monetary' => TRUE,
1374 'is_active' => TRUE,
1375 'is_email_receipt' => TRUE,
1376 'receipt_from_email' => 'yourconscience@donate.com',
1377 'receipt_from_name' => 'Ego Freud',
1378 ];
1379
1380 $page1 = $this->callAPISuccess('ContributionPage', 'create', $params);
1381
1382 //Execute CRM_Contribute_Form_ContributionBase preProcess
1383 //and check the assignment of payment processors
1384 $form = new CRM_Contribute_Form_ContributionBase();
1385 $form->controller = new CRM_Core_Controller();
1386 $form->set('id', $page1['id']);
1387 $_REQUEST['id'] = $page1['id'];
1388
1389 $form->preProcess();
1390 $this->assertEquals('pay_later', $form->_paymentProcessor['name']);
1391
1392 //Disable all the payment processor for the contribution page.
1393 $params['is_pay_later'] = 0;
1394 $page2 = $this->callAPISuccess('ContributionPage', 'create', $params);
1395
1396 //Assert an exception is thrown on loading the contribution page.
1397 $form = new CRM_Contribute_Form_ContributionBase();
1398 $form->controller = new CRM_Core_Controller();
1399 $_REQUEST['id'] = $page2['id'];
1400 $form->set('id', $page2['id']);
1401 $form->preProcess();
1402 }
1403
1404 /**
1405 * function to test card_type and pan truncation.
1406 */
1407 public function testCardTypeAndPanTruncationLiveMode() {
1408 $visaID = CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_FinancialTrxn', 'card_type_id', 'Visa');
1409 $form = new CRM_Contribute_Form_Contribution();
1410 $form->_mode = 'Live';
1411 $form->testSubmit(
1412 [
1413 'total_amount' => 50,
1414 'financial_type_id' => 1,
1415 'contact_id' => $this->_individualId,
1416 'credit_card_number' => 4444333322221111,
1417 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments),
1418 'cvv2' => 123,
1419 'credit_card_exp_date' => [
1420 'M' => 9,
1421 'Y' => date('Y', strtotime('+5 years')),
1422 ],
1423 'credit_card_type' => 'Visa',
1424 'billing_first_name' => 'Junko',
1425 'billing_middle_name' => '',
1426 'billing_last_name' => 'Adams',
1427 'billing_street_address-5' => '790L Lincoln St S',
1428 'billing_city-5' => 'Maryknoll',
1429 'billing_state_province_id-5' => 1031,
1430 'billing_postal_code-5' => 10545,
1431 'billing_country_id-5' => 1228,
1432 'frequency_interval' => 1,
1433 'frequency_unit' => 'month',
1434 'installments' => '',
1435 'hidden_AdditionalDetail' => 1,
1436 'hidden_Premium' => 1,
1437 'from_email_address' => '"civi45" <civi45@civicrm.com>',
1438 'receipt_date' => '',
1439 'receipt_date_time' => '',
1440 'payment_processor_id' => $this->paymentProcessorID,
1441 'currency' => 'USD',
1442 'source' => 'bob sled race',
1443 ],
1444 CRM_Core_Action::ADD
1445 );
1446 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId]);
1447 $lastFinancialTrxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($contribution['id'], 'DESC');
1448 $financialTrxn = $this->callAPISuccessGetSingle(
1449 'FinancialTrxn',
1450 [
1451 'id' => $lastFinancialTrxnId['financialTrxnId'],
1452 'return' => ['card_type_id', 'pan_truncation'],
1453 ]
1454 );
1455 $this->assertEquals($visaID, $financialTrxn['card_type_id']);
1456 $this->assertEquals(1111, $financialTrxn['pan_truncation']);
1457 }
1458
1459 /**
1460 * CRM-21711 Test that custom fields on relevant memberships get updated wehn
1461 * updating multiple memberships
1462 *
1463 * @throws \CRM_Core_Exception
1464 * @throws \CiviCRM_API3_Exception
1465 * @throws \Civi\API\Exception\UnauthorizedException
1466 */
1467 public function testCustomFieldsOnMembershipGetUpdated(): void {
1468 $contactID = $this->individualCreate();
1469 $contactID1 = $this->organizationCreate();
1470 $contactID2 = $this->organizationCreate();
1471
1472 // create membership types
1473 $membershipTypeOne = civicrm_api3('MembershipType', 'create', [
1474 'domain_id' => 1,
1475 'name' => 'One',
1476 'member_of_contact_id' => $contactID1,
1477 'duration_unit' => 'year',
1478 'minimum_fee' => 50,
1479 'duration_interval' => 1,
1480 'period_type' => 'fixed',
1481 'fixed_period_start_day' => '101',
1482 'fixed_period_rollover_day' => '1231',
1483 'financial_type_id' => 1,
1484 'weight' => 50,
1485 'is_active' => 1,
1486 'visibility' => 'Public',
1487 ]);
1488
1489 $membershipTypeTwo = civicrm_api3('MembershipType', 'create', [
1490 'domain_id' => 1,
1491 'name' => 'Two',
1492 'member_of_contact_id' => $contactID2,
1493 'duration_unit' => 'year',
1494 'minimum_fee' => 50,
1495 'duration_interval' => 1,
1496 'period_type' => 'fixed',
1497 'fixed_period_start_day' => '101',
1498 'fixed_period_rollover_day' => '1231',
1499 'financial_type_id' => 1,
1500 'weight' => 51,
1501 'is_active' => 1,
1502 'visibility' => 'Public',
1503 ]);
1504
1505 //create custom Fields
1506 $membershipCustomFieldsGroup = civicrm_api3('CustomGroup', 'create', [
1507 'title' => 'Custom Fields on Membership',
1508 'extends' => 'Membership',
1509 ]);
1510
1511 $membershipCustomField = civicrm_api3('CustomField', 'create', [
1512 'custom_group_id' => $membershipCustomFieldsGroup['id'],
1513 'name' => 'my_membership_custom_field',
1514 'label' => 'Membership Custom Field',
1515 'data_type' => 'String',
1516 'html_type' => 'Text',
1517 'is_active' => TRUE,
1518 'text_length' => 255,
1519 ]);
1520
1521 // create profile
1522 $membershipCustomFieldsProfile = civicrm_api3('UFGroup', 'create', [
1523 "is_active" => "1",
1524 "group_type" => "Membership,Individual",
1525 "title" => "Membership Custom Fields",
1526 "add_captcha" => "0",
1527 "is_map" => "0",
1528 "is_edit_link" => "0",
1529 "is_uf_link" => "0",
1530 "is_update_dupe" => "0",
1531 ]);
1532
1533 // add custom fields to profile
1534 civicrm_api3('UFField', 'create', [
1535 "uf_group_id" => $membershipCustomFieldsProfile['id'],
1536 "field_name" => "custom_" . $membershipCustomField['id'],
1537 "is_active" => "1",
1538 "visibility" => "User and User Admin Only",
1539 "in_selector" => "0",
1540 "is_searchable" => "0",
1541 "label" => "custom text field on membership",
1542 "field_type" => "Membership",
1543 ]);
1544
1545 $contribPage = civicrm_api3('ContributionPage', 'create', [
1546 "title" => "Membership",
1547 "financial_type_id" => 1,
1548 'financial_account_id' => 1,
1549 "is_credit_card_only" => "0",
1550 "is_monetary" => "0",
1551 "is_recur" => "0",
1552 "is_confirm_enabled" => "1",
1553 "is_recur_interval" => "0",
1554 "is_recur_installments" => "0",
1555 "adjust_recur_start_date" => "0",
1556 "is_pay_later" => "1",
1557 "pay_later_text" => "I will send payment by check",
1558 "is_partial_payment" => "0",
1559 "is_email_receipt" => "0",
1560 "is_active" => "1",
1561 "amount_block_is_active" => "0",
1562 "currency" => "USD",
1563 "is_share" => "0",
1564 "is_billing_required" => "0",
1565 "contribution_type_id" => "2",
1566 'is_allow_other_amount' => 1,
1567 'min_amount' => 10,
1568 'max_amount' => 1000,
1569 ]);
1570 $contribPage1 = $contribPage['id'];
1571
1572 //create price set with two options for the two different memberships
1573 $priceSet = civicrm_api3('PriceSet', 'create', [
1574 'title' => "Two Membership Type Checkbox",
1575 'extends' => "CiviMember",
1576 'is_active' => 1,
1577 "financial_type_id" => "1",
1578 ]);
1579 CRM_Core_DAO::executeQuery("INSERT INTO civicrm_price_set_entity (entity_table, entity_id, price_set_id) VALUES('civicrm_contribution_page', $contribPage1, {$priceSet['id']})");
1580
1581 $priceField = civicrm_api3('PriceField', 'create', [
1582 'price_set_id' => $priceSet['id'],
1583 'name' => 'mt',
1584 "label" => "Membership Types",
1585 "html_type" => "CheckBox",
1586 "is_enter_qty" => "0",
1587 "weight" => "1",
1588 "is_display_amounts" => "1",
1589 "options_per_line" => "1",
1590 "is_active" => "1",
1591 "is_required" => "0",
1592 "visibility_id" => "1",
1593 ]);
1594
1595 $priceFieldOption1 = civicrm_api3('PriceFieldValue', 'create', [
1596 "price_field_id" => $priceField['id'],
1597 "name" => "membership_type_one",
1598 "label" => "Membership Type One",
1599 "amount" => "50",
1600 "weight" => "1",
1601 "membership_type_id" => $membershipTypeOne['id'],
1602 "membership_num_terms" => "1",
1603 "is_default" => "0",
1604 "is_active" => "1",
1605 "financial_type_id" => "1",
1606 "non_deductible_amount" => "0.00",
1607 "contribution_type_id" => "2",
1608 ]);
1609
1610 $priceFieldOption2 = civicrm_api3('PriceFieldValue', 'create', [
1611 "price_field_id" => $priceField['id'],
1612 "name" => "membership_type_two",
1613 "label" => "Membership Type Two",
1614 "amount" => "50",
1615 "weight" => "1",
1616 "membership_type_id" => $membershipTypeTwo['id'],
1617 "membership_num_terms" => "1",
1618 "is_default" => "0",
1619 "is_active" => "1",
1620 "financial_type_id" => "1",
1621 "non_deductible_amount" => "0.00",
1622 "contribution_type_id" => "2",
1623 ]);
1624
1625 // assign profile with custom fields to contribution page
1626 civicrm_api3('UFJoin', 'create', [
1627 'module' => "CiviContribute",
1628 'weight' => "1",
1629 'uf_group_id' => $membershipCustomFieldsProfile['id'],
1630 "entity_table" => "civicrm_contribution_page",
1631 "entity_id" => $contribPage1,
1632 ]);
1633
1634 $form = new CRM_Contribute_Form_Contribution_Confirm();
1635 $form->_params = [
1636 'id' => $contribPage1,
1637 'qfKey' => "donotcare",
1638 "custom_{$membershipCustomField['id']}" => "Hello",
1639 "priceSetId" => $priceSet['id'],
1640 'price_set_id' => $priceSet['id'],
1641 "price_" . $priceField['id'] => [$priceFieldOption1['id'] => 1, $priceFieldOption2['id'] => 1],
1642 'invoiceID' => "9a6f7b49358dc31c3604e463b225c5be",
1643 'email' => "admin@example.com",
1644 "currencyID" => "USD",
1645 'description' => "Membership Contribution",
1646 'contact_id' => $contactID,
1647 'skipLineItem' => 0,
1648 'email-5' => 'test@test.com',
1649 'amount' => 100,
1650 'tax_amount' => 0.00,
1651 'is_pay_later' => 1,
1652 'is_quick_config' => 1,
1653 ];
1654 $form->submit($form->_params);
1655 $membership1 = civicrm_api3('Membership', 'getsingle', [
1656 'contact_id' => $contactID,
1657 'membership_type_id' => $membershipTypeOne['id'],
1658 ]);
1659 $this->assertEquals("Hello", $membership1["custom_{$membershipCustomField['id']}"]);
1660
1661 $membership2 = civicrm_api3('Membership', 'getsingle', [
1662 'contact_id' => $contactID,
1663 'membership_type_id' => $membershipTypeTwo['id'],
1664 ]);
1665 $this->assertEquals("Hello", $membership2["custom_{$membershipCustomField['id']}"]);
1666 }
1667
1668 /**
1669 * Test non-membership donation on a contribution page
1670 * using membership priceset.
1671 */
1672 public function testDonationOnMembershipPagePriceset() {
1673 $contactID = $this->individualCreate();
1674 $this->createPriceSetWithPage();
1675 $form = new CRM_Contribute_Form_Contribution_Confirm();
1676 $form->controller = new CRM_Core_Controller();
1677 $form->_params = [
1678 'id' => $this->_ids['contribution_page'],
1679 "qfKey" => "donotcare",
1680 "priceSetId" => $this->_ids['price_set'],
1681 'price_set_id' => $this->_ids['price_set'],
1682 "price_" . $this->_ids['price_field'][0] => $this->_ids['price_field_value']['cont'],
1683 "invoiceID" => "9a6f7b49358dc31c3604e463b225c5be",
1684 "email" => "admin@example.com",
1685 "currencyID" => "USD",
1686 'description' => "Membership Contribution",
1687 'contact_id' => $contactID,
1688 'select_contact_id' => $contactID,
1689 'useForMember' => 1,
1690 'skipLineItem' => 0,
1691 'email-5' => 'test@test.com',
1692 'amount' => 10,
1693 'tax_amount' => NULL,
1694 'is_pay_later' => 1,
1695 'is_quick_config' => 1,
1696 ];
1697 $form->submit($form->_params);
1698
1699 $contribution = $this->callAPISuccessGetSingle('Contribution', [
1700 'contact_id' => $contactID,
1701 ]);
1702 //Check no membership is created.
1703 $this->callAPIFailure('Membership', 'getsingle', [
1704 'contact_id' => $contactID,
1705 ]);
1706 $this->contributionDelete($contribution['id']);
1707
1708 //Choose Membership Priceset
1709 $form->_params["price_{$this->_ids['price_field'][0]}"] = $this->_ids['price_field_value'][0];
1710 $form->_params["amount"] = 20;
1711 $form->submit($form->_params);
1712
1713 $contribution = $this->callAPISuccessGetSingle('Contribution', [
1714 'contact_id' => $contactID,
1715 ]);
1716 //Check membership is created for the contact.
1717 $membership = $this->callAPISuccessGetSingle('Membership', [
1718 'contact_id' => $contactID,
1719 ]);
1720 $membershipPayment = $this->callAPISuccessGetSingle('MembershipPayment', [
1721 'contribution_id' => $contribution['id'],
1722 ]);
1723 $this->assertEquals($membershipPayment['membership_id'], $membership['id']);
1724 $this->membershipDelete($membership['id']);
1725 }
1726
1727 /**
1728 * Test no warnings or errors during preProcess when editing.
1729 */
1730 public function testPreProcessContributionEdit() {
1731 // Simulate a contribution in pending status
1732 $contribution = $this->callAPISuccess(
1733 'Contribution',
1734 'create',
1735 array_merge($this->_params, ['contribution_status_id' => 'Pending'])
1736 );
1737
1738 // set up the form to edit the contribution and call preProcess
1739 $form = $this->getFormObject('CRM_Contribute_Form_Contribution');
1740 $_REQUEST['cid'] = $this->_individualId;
1741 $_REQUEST['id'] = $contribution['id'];
1742 $form->_action = CRM_Core_Action::UPDATE;
1743 $form->preProcess();
1744
1745 // Check something while we're here
1746 $this->assertEquals($contribution['id'], $form->_values['contribution_id']);
1747
1748 unset($_REQUEST['cid']);
1749 unset($_REQUEST['id']);
1750 }
1751
1752 /**
1753 * Mostly just check there's no errors opening the Widget tab on contribution
1754 * pages.
1755 */
1756 public function testOpeningWidgetAdminPage() {
1757 $page_id = $this->callAPISuccess('ContributionPage', 'create', [
1758 'title' => 'my page',
1759 'financial_type_id' => $this->_financialTypeId,
1760 'payment_processor' => $this->paymentProcessorID,
1761 ])['id'];
1762
1763 $form = new CRM_Contribute_Form_ContributionPage_Widget();
1764 $form->controller = new CRM_Core_Controller_Simple('CRM_Contribute_Form_ContributionPage_Widget', 'Widget');
1765
1766 $form->set('reset', '1');
1767 $form->set('action', 'update');
1768 $form->set('id', $page_id);
1769
1770 ob_start();
1771 $form->controller->_actions['display']->perform($form, 'display');
1772 $contents = ob_get_contents();
1773 ob_end_clean();
1774
1775 // The page contents load later by ajax, so there's just the surrounding
1776 // html available now, but we can check at least one thing while we're here.
1777 $this->assertStringContainsString("mainTabContainer", $contents);
1778 }
1779
1780 /**
1781 * Test AdditionalInfo::postProcessCommon
1782 * @dataProvider additionalInfoProvider
1783 * @param array $input
1784 * @param array $expectedFormatted
1785 */
1786 public function testAdditionalInfoPostProcessCommon(array $input, array $expectedFormatted) {
1787 $formatted = [];
1788 $dummy = new CRM_Contribute_Form_AdditionalInfo();
1789 CRM_Contribute_Form_AdditionalInfo::postProcessCommon($input, $formatted, $dummy);
1790 $this->assertEquals($expectedFormatted, $formatted);
1791 }
1792
1793 /**
1794 * Dataprovider for testAdditionalInfoPostProcessCommon
1795 * @return array
1796 */
1797 public function additionalInfoProvider(): array {
1798 return [
1799 'no-date' => [
1800 'input' => [
1801 'qfKey' => 'CRMContributeFormContributionu2pbzqqmz74oscck4ss4osccw4wgccc884wkk4ws0o8wgss4w_8953',
1802 'entryURL' => 'http://example.org/civicrm/contact/view/contribution?reset=1&amp;action=add&amp;cid=1&amp;context=contribution',
1803 'check_number' => '',
1804 'frequency_interval' => '1',
1805 'hidden_AdditionalDetail' => '1',
1806 'contact_id' => '1',
1807 'financial_type_id' => '1',
1808 'payment_instrument_id' => '4',
1809 'trxn_id' => '',
1810 'from_email_address' => '2',
1811 'contribution_status_id' => '1',
1812 // This is unused here but is iffy to put in a dataprovider
1813 'receive_date' => '2021-01-14 11:12:13',
1814 'receipt_date' => '',
1815 'cancel_date' => '',
1816 'cancel_reason' => '',
1817 'price_set_id' => '',
1818 'total_amount' => 10,
1819 'currency' => 'USD',
1820 'source' => 'a source',
1821 'soft_credit_contact_id' => [
1822 1 => '',
1823 2 => '',
1824 3 => '',
1825 4 => '',
1826 5 => '',
1827 6 => '',
1828 7 => '',
1829 8 => '',
1830 9 => '',
1831 10 => '',
1832 ],
1833 'soft_credit_amount' => [
1834 1 => '',
1835 2 => '',
1836 3 => '',
1837 4 => '',
1838 5 => '',
1839 6 => '',
1840 7 => '',
1841 8 => '',
1842 9 => '',
1843 10 => '',
1844 ],
1845 'soft_credit_type' => [
1846 1 => '',
1847 2 => '',
1848 3 => '',
1849 4 => '',
1850 5 => '',
1851 6 => '',
1852 7 => '',
1853 8 => '',
1854 9 => '',
1855 10 => '',
1856 ],
1857 'sct_default_id' => '3',
1858 'MAX_FILE_SIZE' => '2097152',
1859 'ip_address' => '127.0.0.1',
1860 'price_1' => [
1861 1 => 1,
1862 ],
1863 'amount' => 10,
1864 ],
1865 'expected' => [
1866 'non_deductible_amount' => NULL,
1867 'total_amount' => 10,
1868 'fee_amount' => NULL,
1869 'trxn_id' => '',
1870 'invoice_id' => NULL,
1871 'creditnote_id' => NULL,
1872 'campaign_id' => NULL,
1873 'contribution_page_id' => NULL,
1874 'thankyou_date' => 'null',
1875 'custom' => [],
1876 ],
1877 ],
1878
1879 'date-no-time' => [
1880 'input' => [
1881 'qfKey' => 'CRMContributeFormContributionu2pbzqqmz74oscck4ss4osccw4wgccc884wkk4ws0o8wgss4w_8953',
1882 'entryURL' => 'http://example.org/civicrm/contact/view/contribution?reset=1&amp;action=add&amp;cid=1&amp;context=contribution',
1883 'id' => '40',
1884 'frequency_interval' => '1',
1885 'hidden_AdditionalDetail' => '1',
1886 'thankyou_date' => '2021-01-14',
1887 'non_deductible_amount' => '0.00',
1888 'fee_amount' => '0.00',
1889 'invoice_id' => '',
1890 'creditnote_id' => '',
1891 'contribution_page_id' => '',
1892 'note' => '',
1893 'contact_id' => '1',
1894 'financial_type_id' => '1',
1895 'from_email_address' => '2',
1896 'contribution_status_id' => '1',
1897 // This is unused here but is iffy to put in a dataprovider
1898 'receive_date' => '2021-01-14 11:12:13',
1899 'receipt_date' => '',
1900 'cancel_date' => '',
1901 'cancel_reason' => '',
1902 'price_set_id' => '',
1903 'total_amount' => '10.00',
1904 'currency' => 'USD',
1905 'source' => 'a source',
1906 'soft_credit_contact_id' => [
1907 1 => '',
1908 2 => '',
1909 3 => '',
1910 4 => '',
1911 5 => '',
1912 6 => '',
1913 7 => '',
1914 8 => '',
1915 9 => '',
1916 10 => '',
1917 ],
1918 'soft_credit_amount' => [
1919 1 => '',
1920 2 => '',
1921 3 => '',
1922 4 => '',
1923 5 => '',
1924 6 => '',
1925 7 => '',
1926 8 => '',
1927 9 => '',
1928 10 => '',
1929 ],
1930 'soft_credit_type' => [
1931 1 => '',
1932 2 => '',
1933 3 => '',
1934 4 => '',
1935 5 => '',
1936 6 => '',
1937 7 => '',
1938 8 => '',
1939 9 => '',
1940 10 => '',
1941 ],
1942 'sct_default_id' => '3',
1943 'MAX_FILE_SIZE' => '2097152',
1944 'ip_address' => '127.0.0.1',
1945 // leaving out since don't want to enforce string 'null' in a test
1946 //'tax_amount' => 'null',
1947 ],
1948 'expected' => [
1949 'non_deductible_amount' => '0.00',
1950 'total_amount' => '10.00',
1951 'fee_amount' => '0.00',
1952 'trxn_id' => NULL,
1953 'invoice_id' => '',
1954 'creditnote_id' => '',
1955 'campaign_id' => NULL,
1956 'contribution_page_id' => NULL,
1957 'thankyou_date' => '20210114000000',
1958 'custom' => [],
1959 ],
1960 ],
1961
1962 'date-and-time' => [
1963 'input' => [
1964 'qfKey' => 'CRMContributeFormContributionu2pbzqqmz74oscck4ss4osccw4wgccc884wkk4ws0o8wgss4w_8953',
1965 'entryURL' => 'http://example.org/civicrm/contact/view/contribution?reset=1&amp;action=add&amp;cid=1&amp;context=contribution',
1966 'id' => '40',
1967 'frequency_interval' => '1',
1968 'hidden_AdditionalDetail' => '1',
1969 'thankyou_date' => '2021-01-14 10:11:12',
1970 'non_deductible_amount' => '0.00',
1971 'fee_amount' => '0.00',
1972 'invoice_id' => '',
1973 'creditnote_id' => '',
1974 'contribution_page_id' => '',
1975 'note' => '',
1976 'contact_id' => '1',
1977 'financial_type_id' => '1',
1978 'from_email_address' => '2',
1979 'contribution_status_id' => '1',
1980 // This is unused here but is iffy to put in a dataprovider
1981 'receive_date' => '2021-01-14 11:12:13',
1982 'receipt_date' => '',
1983 'cancel_date' => '',
1984 'cancel_reason' => '',
1985 'price_set_id' => '',
1986 'total_amount' => '10.00',
1987 'currency' => 'USD',
1988 'source' => 'a source',
1989 'soft_credit_contact_id' => [
1990 1 => '',
1991 2 => '',
1992 3 => '',
1993 4 => '',
1994 5 => '',
1995 6 => '',
1996 7 => '',
1997 8 => '',
1998 9 => '',
1999 10 => '',
2000 ],
2001 'soft_credit_amount' => [
2002 1 => '',
2003 2 => '',
2004 3 => '',
2005 4 => '',
2006 5 => '',
2007 6 => '',
2008 7 => '',
2009 8 => '',
2010 9 => '',
2011 10 => '',
2012 ],
2013 'soft_credit_type' => [
2014 1 => '',
2015 2 => '',
2016 3 => '',
2017 4 => '',
2018 5 => '',
2019 6 => '',
2020 7 => '',
2021 8 => '',
2022 9 => '',
2023 10 => '',
2024 ],
2025 'sct_default_id' => '3',
2026 'MAX_FILE_SIZE' => '2097152',
2027 'ip_address' => '127.0.0.1',
2028 // leaving out since don't want to enforce string 'null' in a test
2029 //'tax_amount' => 'null',
2030 ],
2031 'expected' => [
2032 'non_deductible_amount' => '0.00',
2033 'total_amount' => '10.00',
2034 'fee_amount' => '0.00',
2035 'trxn_id' => NULL,
2036 'invoice_id' => '',
2037 'creditnote_id' => '',
2038 'campaign_id' => NULL,
2039 'contribution_page_id' => NULL,
2040 'thankyou_date' => '20210114101112',
2041 'custom' => [],
2042 ],
2043 ],
2044 ];
2045 }
2046
2047 /**
2048 * Test formRule
2049 */
2050 public function testContributionFormRule() {
2051 $fields = [
2052 'contact_id' => $this->_individualId,
2053 'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Donation'),
2054 'currency' => 'USD',
2055 'total_amount' => '10',
2056 'price_set_id' => '',
2057 'source' => '',
2058 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
2059 'cancel_date' => '',
2060 'cancel_reason' => '',
2061 'receive_date' => date('Y-m-d H:i:s'),
2062 'from_email_address' => key(CRM_Core_BAO_Email::getFromEmail()),
2063 'receipt_date' => '',
2064 'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
2065 'trxn_id' => '',
2066 'check_number' => '',
2067 'soft_credit_contact_id' => [
2068 1 => '',
2069 2 => '',
2070 3 => '',
2071 4 => '',
2072 5 => '',
2073 6 => '',
2074 7 => '',
2075 8 => '',
2076 9 => '',
2077 10 => '',
2078 ],
2079 'soft_credit_amount' => [
2080 1 => '',
2081 2 => '',
2082 3 => '',
2083 4 => '',
2084 5 => '',
2085 6 => '',
2086 7 => '',
2087 8 => '',
2088 9 => '',
2089 10 => '',
2090 ],
2091 'soft_credit_type' => [
2092 1 => '',
2093 2 => '',
2094 3 => '',
2095 4 => '',
2096 5 => '',
2097 6 => '',
2098 7 => '',
2099 8 => '',
2100 9 => '',
2101 10 => '',
2102 ],
2103 ];
2104
2105 $form = new CRM_Contribute_Form_Contribution();
2106 $this->assertSame([], $form::formRule($fields, [], $form));
2107 }
2108
2109 /**
2110 * Check that formRule validates you can only have one contribution with a
2111 * given trxn_id.
2112 */
2113 public function testContributionFormRuleDuplicateTrxn() {
2114 $contribution = $this->callAPISuccess('Contribution', 'create', array_merge($this->_params, ['trxn_id' => '1234']));
2115
2116 $fields = [
2117 'contact_id' => $this->_individualId,
2118 'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Donation'),
2119 'currency' => 'USD',
2120 'total_amount' => '10',
2121 'price_set_id' => '',
2122 'source' => '',
2123 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
2124 'cancel_date' => '',
2125 'cancel_reason' => '',
2126 'receive_date' => date('Y-m-d H:i:s'),
2127 'from_email_address' => key(CRM_Core_BAO_Email::getFromEmail()),
2128 'receipt_date' => '',
2129 'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
2130 'trxn_id' => '1234',
2131 'check_number' => '',
2132 'soft_credit_contact_id' => [
2133 1 => '',
2134 2 => '',
2135 3 => '',
2136 4 => '',
2137 5 => '',
2138 6 => '',
2139 7 => '',
2140 8 => '',
2141 9 => '',
2142 10 => '',
2143 ],
2144 'soft_credit_amount' => [
2145 1 => '',
2146 2 => '',
2147 3 => '',
2148 4 => '',
2149 5 => '',
2150 6 => '',
2151 7 => '',
2152 8 => '',
2153 9 => '',
2154 10 => '',
2155 ],
2156 'soft_credit_type' => [
2157 1 => '',
2158 2 => '',
2159 3 => '',
2160 4 => '',
2161 5 => '',
2162 6 => '',
2163 7 => '',
2164 8 => '',
2165 9 => '',
2166 10 => '',
2167 ],
2168 ];
2169
2170 $form = new CRM_Contribute_Form_Contribution();
2171 $this->assertEquals(['trxn_id' => "Transaction ID's must be unique. Transaction '1234' already exists in your database."], $form->formRule($fields, [], $form));
2172 }
2173
2174 /**
2175 * Get the contribution form object.
2176 *
2177 * @param array $formValues
2178 *
2179 * @return \CRM_Contribute_Form_Contribution
2180 */
2181 protected function getContributionForm(array $formValues): CRM_Contribute_Form_Contribution {
2182 /* @var CRM_Contribute_Form_Contribution $form */
2183 $form = $this->getFormObject('CRM_Contribute_Form_Contribution', $formValues);
2184 $form->buildForm();
2185 return $form;
2186 }
2187
2188 /**
2189 * Get the payment instrument ID.
2190 *
2191 * Function just exists to avoid line-wrapping hell with the
2192 * longer function it calls.
2193 *
2194 * @param string $name
2195 *
2196 * @return int
2197 */
2198 protected function getPaymentInstrument(string $name): int {
2199 return CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', $name);
2200 }
2201
2202 }