3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
13 * Test APIv3 civicrm_contribute_* functions
15 * @package CiviCRM_APIv3
16 * @subpackage API_Contribution
19 class CRM_Contribute_Form_ContributionTest
extends CiviUnitTestCase
{
20 use CRMTraits_PCP_PCPTestTrait
;
22 protected $_individualId;
23 protected $_contribution;
24 protected $_financialTypeId = 1;
25 protected $_entity = 'Contribution';
28 protected $_pageParams = [];
32 * Parameters to create payment processor.
36 protected $_processorParams = [];
39 * ID of created event.
46 * Payment instrument mapping.
50 protected $paymentInstruments = [];
57 protected $products = [];
60 * Dummy payment processor.
62 * @var CRM_Core_Payment_Dummy
64 protected $paymentProcessor;
67 * Payment processor ID.
71 protected $paymentProcessorID;
76 * @throws \CRM_Core_Exception
77 * @throws \CiviCRM_API3_Exception
79 public function setUp(): void
{
80 $this->_apiversion
= 3;
82 $this->_userId
= $this->createLoggedInUser();
84 $this->_individualId
= $this->ids
['contact'][0] = $this->individualCreate();
86 'contact_id' => $this->_individualId
,
87 'receive_date' => '20120511',
88 'total_amount' => 100.00,
89 'financial_type_id' => $this->_financialTypeId
,
90 'non_deductible_amount' => 10.00,
92 'net_amount' => 95.00,
94 'contribution_status_id' => 1,
96 $this->_processorParams
= [
99 'payment_processor_type_id' => 10,
100 'financial_account_id' => 12,
103 'url_site' => 'http://dummy.com',
104 'url_recur' => 'http://dummy.com',
108 $instruments = $this->callAPISuccess('contribution', 'getoptions', ['field' => 'payment_instrument_id']);
109 $this->paymentInstruments
= $instruments['values'];
110 $product1 = $this->callAPISuccess('product', 'create', [
112 'options' => 'brainy smurf, clumsy smurf, papa smurf',
115 $this->products
[] = $product1['values'][$product1['id']];
116 $this->paymentProcessor
= $this->dummyProcessorCreate();
117 $processor = $this->paymentProcessor
->getPaymentProcessor();
118 $this->paymentProcessorID
= $processor['id'];
122 * Clean up after each test.
124 * @throws \CRM_Core_Exception
126 public function tearDown(): void
{
127 $this->quickCleanUpFinancialEntities();
128 $this->quickCleanup(['civicrm_note', 'civicrm_uf_match', 'civicrm_address']);
132 * CHeck that all tests that have created payments have created them with the right financial entities.
134 * @throws \CRM_Core_Exception
136 protected function assertPostConditions() {
137 $this->validateAllPayments();
141 * Test the submit function on the contribution page.
143 * @param string $thousandSeparator
145 * @dataProvider getThousandSeparators
147 public function testSubmit($thousandSeparator) {
148 $this->setCurrencySeparators($thousandSeparator);
149 $form = new CRM_Contribute_Form_Contribution();
151 'total_amount' => $this->formatMoneyInput(1234),
152 'financial_type_id' => 1,
153 'contact_id' => $this->_individualId
,
154 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
155 'contribution_status_id' => 1,
156 ], CRM_Core_Action
::ADD
);
157 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId
]);
158 $this->assertEmpty($contribution['amount_level']);
159 $this->assertEquals(1234, $contribution['total_amount']);
160 $this->assertEquals(1234, $contribution['net_amount']);
164 * Test the submit function on the contribution page.
166 public function testSubmitCreditCard() {
167 $form = new CRM_Contribute_Form_Contribution();
169 'total_amount' => 50,
170 'financial_type_id' => 1,
171 'contact_id' => $this->_individualId
,
172 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments
),
173 'contribution_status_id' => 1,
174 ], CRM_Core_Action
::ADD
);
175 $this->callAPISuccessGetCount('Contribution', [
176 'contact_id' => $this->_individualId
,
177 'contribution_status_id' => 'Completed',
182 * Test the submit function on the contribution page.
184 public function testSubmitCreditCardPayPal() {
185 $mut = new CiviMailUtils($this, TRUE);
186 $mut->clearMessages();
187 $form = new CRM_Contribute_Form_Contribution();
188 $paymentProcessorID = $this->paymentProcessorCreate(['is_test' => 0]);
189 $form->_mode
= 'Live';
193 'total_amount' => 50,
194 'financial_type_id' => 1,
195 'contact_id' => $this->_individualId
,
196 'contribution_status_id' => 1,
197 'credit_card_number' => 4444333322221111,
199 'credit_card_exp_date' => [
203 'credit_card_type' => 'Visa',
204 'billing_first_name' => 'Junko',
205 'billing_middle_name' => '',
206 'billing_last_name' => 'Adams',
207 'billing_street_address-5' => '790L Lincoln St S',
208 'billing_city-5' => 'Maryknoll',
209 'billing_state_province_id-5' => 1031,
210 'billing_postal_code-5' => 10545,
211 'billing_country_id-5' => 1228,
212 'frequency_interval' => 1,
213 'frequency_unit' => 'month',
214 'installments' => '',
215 'hidden_AdditionalDetail' => 1,
216 'hidden_Premium' => 1,
217 'from_email_address' => '"civi45" <civi45@civicrm.com>',
218 'is_email_receipt' => TRUE,
219 'receipt_date' => '',
220 'receipt_date_time' => '',
221 'payment_processor_id' => $paymentProcessorID,
223 'source' => 'bob sled race',
224 ], CRM_Core_Action
::ADD
);
226 catch (Civi\Payment\Exception\PaymentProcessorException
$e) {
230 $contribution = $this->callAPISuccess('Contribution', 'get', [
231 'contact_id' => $this->_individualId
,
232 'contribution_status_id' => $error ?
'Failed' : 'Completed',
233 'payment_instrument_id' => $this->callAPISuccessGetValue('PaymentProcessor', [
234 'return' => 'payment_instrument_id',
235 'id' => $paymentProcessorID,
239 $this->assertEquals(1, $contribution["count"], "Contribution count should be one.");
240 $this->assertTrue(!empty($contribution["values"][$contribution["id"]]["receipt_date"]), "Receipt date should not be blank.");
242 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $this->_individualId
]);
243 $this->assertTrue(empty($contact['source']));
245 $msgs = $mut->getAllMessages();
246 $this->assertEquals(1, count($msgs));
248 $mut->clearMessages();
253 * Test the submit function on the contribution page
255 public function testSubmitCreditCardWithEmailReceipt() {
256 $mut = new CiviMailUtils($this, TRUE);
257 $mut->clearMessages();
258 $form = new CRM_Contribute_Form_Contribution();
259 $form->_mode
= 'Live';
262 'total_amount' => 50,
263 'financial_type_id' => 1,
264 'contact_id' => $this->_individualId
,
265 'contribution_status_id' => 1,
266 'credit_card_number' => 4444333322221111,
268 'credit_card_exp_date' => [
272 'credit_card_type' => 'Visa',
273 'billing_first_name' => 'Junko',
274 'billing_middle_name' => '',
275 'billing_last_name' => 'Adams',
276 'billing_street_address-5' => '790L Lincoln St S',
277 'billing_city-5' => 'Maryknoll',
278 'billing_state_province_id-5' => 1031,
279 'billing_postal_code-5' => 10545,
280 'billing_country_id-5' => 1228,
281 'frequency_interval' => 1,
282 'frequency_unit' => 'month',
283 'installments' => '',
284 'hidden_AdditionalDetail' => 1,
285 'hidden_Premium' => 1,
286 'from_email_address' => '"civi45" <civi45@civicrm.com>',
287 'is_email_receipt' => TRUE,
288 'receipt_date' => '',
289 'receipt_date_time' => '',
290 'payment_processor_id' => $this->paymentProcessorID
,
292 'source' => 'bob sled race',
293 ], CRM_Core_Action
::ADD
);
295 $this->callAPISuccessGetCount('Contribution', [
296 'contact_id' => $this->_individualId
,
297 'contribution_status_id' => 'Completed',
298 'payment_instrument_id' => $this->callAPISuccessGetValue('PaymentProcessor', [
299 'return' => 'payment_instrument_id',
300 'id' => $this->paymentProcessorID
,
303 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $this->_individualId
]);
304 $this->assertTrue(empty($contact['source']));
305 $msgs = $mut->getAllMessages();
306 $this->assertEquals(1, count($msgs));
311 * Test the submit function on the contribution page.
313 public function testSubmitCreditCardNoReceipt() {
314 $mut = new CiviMailUtils($this, TRUE);
315 $mut->clearMessages();
316 $form = new CRM_Contribute_Form_Contribution();
317 $form->_mode
= 'Live';
321 'total_amount' => 60,
322 'financial_type_id' => 1,
323 'contact_id' => $this->_individualId
,
324 'contribution_status_id' => 1,
325 'credit_card_number' => 4444333322221111,
327 'credit_card_exp_date' => [
331 'credit_card_type' => 'Visa',
332 'billing_first_name' => 'Junko',
333 'billing_middle_name' => '',
334 'billing_last_name' => 'Adams',
335 'billing_street_address-5' => '790L Lincoln St S',
336 'billing_city-5' => 'Maryknoll',
337 'billing_state_province_id-5' => 1031,
338 'billing_postal_code-5' => 10545,
339 'billing_country_id-5' => 1228,
340 'frequency_interval' => 1,
341 'frequency_unit' => 'month',
342 'installments' => '',
343 'hidden_AdditionalDetail' => 1,
344 'hidden_Premium' => 1,
345 'from_email_address' => '"civi45" <civi45@civicrm.com>',
346 'is_email_receipt' => FALSE,
347 'receipt_date' => '',
348 'receipt_date_time' => '',
349 'payment_processor_id' => $this->paymentProcessorID
,
351 'source' => 'bob sled race',
352 ], CRM_Core_Action
::ADD
);
354 catch (Civi\Payment\Exception\PaymentProcessorException
$e) {
358 $this->callAPISuccessGetCount('Contribution', [
359 'contact_id' => $this->_individualId
,
360 'contribution_status_id' => $error ?
'Failed' : 'Completed',
361 'payment_instrument_id' => $this->callAPISuccessGetValue('PaymentProcessor', [
362 'return' => 'payment_instrument_id',
363 'id' => $this->paymentProcessorID
,
366 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $this->_individualId
]);
367 $this->assertTrue(empty($contact['source']));
368 $mut->assertMailLogEmpty();
373 * Test the submit function on the contribution page.
375 public function testSubmitCreditCardFee() {
376 $form = new CRM_Contribute_Form_Contribution();
377 $this->paymentProcessor
->setDoDirectPaymentResult(['payment_status_id' => 1, 'is_error' => 0, 'trxn_id' => 'tx', 'fee_amount' => .08]);
378 $form->_mode
= 'Live';
380 'total_amount' => 50,
381 'financial_type_id' => 1,
382 'contact_id' => $this->_individualId
,
383 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments
),
384 'contribution_status_id' => 1,
385 'credit_card_number' => 4444333322221111,
387 'credit_card_exp_date' => [
391 'credit_card_type' => 'Visa',
392 'billing_first_name' => 'Junko',
393 'billing_middle_name' => '',
394 'billing_last_name' => 'Adams',
395 'billing_street_address-5' => '790L Lincoln St S',
396 'billing_city-5' => 'Maryknoll',
397 'billing_state_province_id-5' => 1031,
398 'billing_postal_code-5' => 10545,
399 'billing_country_id-5' => 1228,
400 'frequency_interval' => 1,
401 'frequency_unit' => 'month',
402 'installments' => '',
403 'hidden_AdditionalDetail' => 1,
404 'hidden_Premium' => 1,
405 'from_email_address' => '"civi45" <civi45@civicrm.com>',
406 'receipt_date' => '',
407 'receipt_date_time' => '',
408 'payment_processor_id' => $this->paymentProcessorID
,
411 ], CRM_Core_Action
::ADD
);
413 $contribution = $this->callAPISuccessGetSingle('Contribution', [
414 'contact_id' => $this->_individualId
,
415 'contribution_status_id' => 'Completed',
417 $this->assertEquals('50.00', $contribution['total_amount']);
418 $this->assertEquals(.08, $contribution['fee_amount']);
419 $this->assertEquals(49.92, $contribution['net_amount']);
420 $this->assertEquals('tx', $contribution['trxn_id']);
421 $this->assertEmpty($contribution['amount_level']);
425 * Test a fully deductible contribution submitted by credit card (CRM-16669).
427 public function testSubmitCreditCardFullyDeductible() {
428 $form = new CRM_Contribute_Form_Contribution();
429 $form->_mode
= 'Live';
431 'total_amount' => 50,
432 'financial_type_id' => 1,
433 'contact_id' => $this->_individualId
,
434 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments
),
435 'contribution_status_id' => 1,
436 'credit_card_number' => 4444333322221111,
438 'credit_card_exp_date' => [
442 'credit_card_type' => 'Visa',
443 'billing_first_name' => 'Junko',
444 'billing_middle_name' => '',
445 'billing_last_name' => 'Adams',
446 'billing_street_address-5' => '790L Lincoln St S',
447 'billing_city-5' => 'Maryknoll',
448 'billing_state_province_id-5' => 1031,
449 'billing_postal_code-5' => 10545,
450 'billing_country_id-5' => 1228,
451 'frequency_interval' => 1,
452 'frequency_unit' => 'month',
453 'installments' => '',
454 'hidden_AdditionalDetail' => 1,
455 'hidden_Premium' => 1,
456 'from_email_address' => '"civi45" <civi45@civicrm.com>',
457 'receipt_date' => '',
458 'receipt_date_time' => '',
459 'payment_processor_id' => $this->paymentProcessorID
,
462 ], CRM_Core_Action
::ADD
);
464 $contribution = $this->callAPISuccessGetSingle('Contribution', [
465 'contact_id' => $this->_individualId
,
466 'contribution_status_id' => 'Completed',
468 $this->assertEquals('50.00', $contribution['total_amount']);
469 $this->assertEquals(0, $contribution['non_deductible_amount']);
473 * Test the submit function with an invalid payment.
475 * We expect the contribution to be created but left pending. The payment has failed.
477 * Test covers CRM-16417 change to keep failed transactions.
480 * - 1 Contribution with status = Pending
482 * - 1 civicrm_financial_item. This is linked to the line item and has a status of 3
484 * @throws \CRM_Core_Exception
485 * @throws \CiviCRM_API3_Exception
487 public function testSubmitCreditCardInvalid() {
488 $form = new CRM_Contribute_Form_Contribution();
489 $this->paymentProcessor
->setDoDirectPaymentResult(['is_error' => 1]);
492 'total_amount' => 50,
493 'financial_type_id' => 1,
494 'contact_id' => $this->_individualId
,
495 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments
),
496 'payment_processor_id' => $this->paymentProcessorID
,
497 'credit_card_exp_date' => ['M' => 5, 'Y' => 2012],
498 'credit_card_number' => '411111111111111',
499 ], CRM_Core_Action
::ADD
, 'live');
501 catch (\Civi\Payment\Exception\PaymentProcessorException
$e) {
502 $this->callAPISuccessGetCount('Contribution', [
503 'contact_id' => $this->_individualId
,
504 'contribution_status_id' => 'Failed',
506 $lineItem = $this->callAPISuccessGetSingle('line_item', []);
507 $this->assertEquals('50.00', $lineItem['unit_price']);
508 $this->assertEquals('50.00', $lineItem['line_total']);
509 $this->assertEquals(1, $lineItem['qty']);
510 $this->assertEquals(1, $lineItem['financial_type_id']);
511 $financialItem = $this->callAPISuccessGetSingle('financial_item', [
512 'civicrm_line_item' => $lineItem['id'],
513 'entity_id' => $lineItem['id'],
515 $this->assertEquals('50.00', $financialItem['amount']);
516 $this->assertEquals(3, $financialItem['status_id']);
519 $this->fail('An expected exception has not been raised.');
523 * Test the submit function creates a billing address if provided.
525 * @throws \CRM_Core_Exception
526 * @throws \CiviCRM_API3_Exception
528 public function testSubmitCreditCardWithBillingAddress() {
529 $form = new CRM_Contribute_Form_Contribution();
531 'total_amount' => 50,
532 'financial_type_id' => 1,
533 'contact_id' => $this->_individualId
,
534 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments
),
535 'payment_processor_id' => $this->paymentProcessorID
,
536 'credit_card_exp_date' => ['M' => 5, 'Y' => 2025],
537 'credit_card_number' => '411111111111111',
538 'billing_city-5' => 'Vancouver',
539 ], CRM_Core_Action
::ADD
, 'live');
540 $contribution = $this->callAPISuccessGetSingle('Contribution', ['return' => 'address_id']);
541 $this->assertNotEmpty($contribution['address_id']);
542 // CRM-18490 : There is a unwanted test leakage due to below getsingle Api as it only fails in Jenkin
543 // for now we are only fetching address on based on Address ID (removed filter location_type_id and city)
544 $this->callAPISuccessGetSingle('Address', [
545 'id' => $contribution['address_id'],
550 * CRM-20745: Test the submit function correctly sets the
551 * receive date for recurring contribution.
553 public function testSubmitCreditCardWithRecur() {
554 $form = new CRM_Contribute_Form_Contribution();
555 $receiveDate = date('Y-m-d H:i:s', strtotime('+1 month'));
557 'total_amount' => 50,
558 'financial_type_id' => 1,
560 'frequency_interval' => 2,
561 'frequency_unit' => 'month',
563 'receive_date' => $receiveDate,
564 'contact_id' => $this->_individualId
,
565 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments
),
566 'payment_processor_id' => $this->paymentProcessorID
,
567 'credit_card_exp_date' => ['M' => 5, 'Y' => 2025],
568 'credit_card_number' => '411111111111111',
569 'billing_city-5' => 'Vancouver',
570 ], CRM_Core_Action
::ADD
, 'live');
571 $contribution = $this->callAPISuccessGetSingle('Contribution', ['return' => 'receive_date']);
572 $this->assertEquals($contribution['receive_date'], $receiveDate);
576 * Test the submit function does not create a billing address if no details provided.
578 public function testSubmitCreditCardWithNoBillingAddress() {
579 $form = new CRM_Contribute_Form_Contribution();
581 'total_amount' => 50,
582 'financial_type_id' => 1,
583 'contact_id' => $this->_individualId
,
584 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments
),
585 'payment_processor_id' => $this->paymentProcessorID
,
586 'credit_card_exp_date' => ['M' => 5, 'Y' => 2025],
587 'credit_card_number' => '411111111111111',
588 ], CRM_Core_Action
::ADD
, 'live');
589 $contribution = $this->callAPISuccessGetSingle('Contribution', ['return' => 'address_id']);
590 $this->assertEmpty($contribution['address_id']);
591 $this->callAPISuccessGetCount('Address', [
592 'city' => 'Vancouver',
593 'location_type_id' => 5,
598 * Test the submit function on the contribution page.
600 * @throws \CRM_Core_Exception
601 * @throws \CiviCRM_API3_Exception
603 public function testSubmitEmailReceipt() {
604 $form = new CRM_Contribute_Form_Contribution();
605 $mut = new CiviMailUtils($this, TRUE);
607 'total_amount' => 50,
608 'financial_type_id' => 1,
609 'contact_id' => $this->_individualId
,
610 'is_email_receipt' => TRUE,
611 'from_email_address' => 'test@test.com',
612 'contribution_status_id' => 1,
613 ], CRM_Core_Action
::ADD
);
614 $this->callAPISuccessGetCount('Contribution', ['contact_id' => $this->_individualId
], 1);
616 'Contribution Information',
622 * Test the submit function on the contribution page using numerical from email address.
624 public function testSubmitEmailReceiptUserEmailFromAddress() {
625 $form = new CRM_Contribute_Form_Contribution();
626 $mut = new CiviMailUtils($this, TRUE);
627 $email = $this->callAPISuccess('Email', 'create', [
628 'contact_id' => $this->_userId
,
629 'email' => 'testLoggedIn@example.com',
632 'total_amount' => 50,
633 'financial_type_id' => 1,
634 'contact_id' => $this->_individualId
,
635 'is_email_receipt' => TRUE,
636 'from_email_address' => $email['id'],
637 'contribution_status_id' => 1,
638 ], CRM_Core_Action
::ADD
);
639 $this->callAPISuccessGetCount('Contribution', ['contact_id' => $this->_individualId
], 1);
641 'Below you will find a receipt for this contribution.',
642 '<testloggedin@example.com>',
648 * Ensure that price field are shown during pay later/pending Contribution
650 public function testEmailReceiptOnPayLater() {
651 $donationFT = CRM_Core_DAO
::getFieldValue('CRM_Financial_DAO_FinancialType', 'Donation', 'id', 'name');
653 'title' => 'Price Set' . substr(sha1(rand()), 0, 4),
655 'financial_type_id' => $donationFT,
658 $paramsSet['name'] = CRM_Utils_String
::titleToVar($paramsSet['title']);
660 $priceset = CRM_Price_BAO_PriceSet
::create($paramsSet);
661 $priceSetId = $priceset->id
;
663 //Checking for priceset added in the table.
664 $this->assertDBCompareValue('CRM_Price_BAO_PriceSet', $priceSetId, 'title',
665 'id', $paramsSet['title'], 'Check DB for created priceset'
668 'label' => 'Price Field',
669 'name' => CRM_Utils_String
::titleToVar('Price Field'),
670 'html_type' => 'CheckBox',
671 'option_label' => ['1' => 'Price Field 1', '2' => 'Price Field 2'],
672 'option_value' => ['1' => 100, '2' => 200],
673 'option_name' => ['1' => 'Price Field 1', '2' => 'Price Field 2'],
674 'option_weight' => ['1' => 1, '2' => 2],
675 'option_amount' => ['1' => 100, '2' => 200],
676 'is_display_amounts' => 1,
678 'options_per_line' => 1,
679 'is_active' => ['1' => 1, '2' => 1],
680 'price_set_id' => $priceset->id
,
682 'financial_type_id' => $donationFT,
684 $priceField = CRM_Price_BAO_PriceField
::create($paramsField);
685 $priceFieldValue = $this->callAPISuccess('PriceFieldValue', 'get', ['price_field_id' => $priceField->id
]);
688 'total_amount' => 100,
689 'financial_type_id' => $donationFT,
690 'contact_id' => $this->_individualId
,
691 'is_email_receipt' => TRUE,
692 'from_email_address' => 'test@test.com',
693 'price_set_id' => $priceSetId,
694 'contribution_status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'),
697 foreach ($priceFieldValue['values'] as $id => $price) {
698 if ($price['amount'] == 100) {
699 $params['price_' . $priceField->id
] = [$id => 1];
702 $form = new CRM_Contribute_Form_Contribution();
703 $mut = new CiviMailUtils($this, TRUE);
704 $form->_priceSet
= current(CRM_Price_BAO_PriceSet
::getSetDetail($priceSetId));
705 $form->testSubmit($params, CRM_Core_Action
::ADD
);
708 'Financial Type: Donation
709 ---------------------------------------------------------
711 ----------------------------------------------------------
712 Price Field - Price Field 1 1 $ 100.00 $ 100.00
719 * Test that a contribution is assigned against a pledge.
721 public function testUpdatePledge() {
722 $pledge = $this->callAPISuccess('pledge', 'create', [
723 'contact_id' => $this->_individualId
,
724 'pledge_create_date' => date('Ymd'),
725 'start_date' => date('Ymd'),
727 'pledge_status_id' => '2',
728 'pledge_financial_type_id' => '1',
729 'pledge_original_installment_amount' => 20,
730 'frequency_interval' => 5,
731 'frequency_unit' => 'year',
732 'frequency_day' => 15,
736 $pledgePaymentID = $this->callAPISuccess('pledge_payment', 'getvalue', [
737 'pledge_id' => $pledge['id'],
738 'options' => ['limit' => 1],
741 $form = new CRM_Contribute_Form_Contribution();
743 'total_amount' => 50,
744 'financial_type_id' => 1,
745 'contact_id' => $this->_individualId
,
746 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
747 'pledge_payment_id' => $pledgePaymentID,
748 'contribution_status_id' => 1,
749 ], CRM_Core_Action
::ADD
);
750 $pledgePayment = $this->callAPISuccess('pledge_payment', 'getsingle', ['id' => $pledgePaymentID]);
751 $this->assertNotEmpty($pledgePayment['contribution_id']);
752 $this->assertEquals($pledgePayment['actual_amount'], 50);
753 $this->assertEquals(1, $pledgePayment['status_id']);
757 * Test functions involving premiums.
759 public function testPremiumUpdate() {
760 $form = new CRM_Contribute_Form_Contribution();
761 $mut = new CiviMailUtils($this, TRUE);
763 'total_amount' => 50,
764 'financial_type_id' => 1,
765 'contact_id' => $this->_individualId
,
766 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
767 'contribution_status_id' => 1,
768 'product_name' => [$this->products
[0]['id'], 1],
769 'fulfilled_date' => '',
770 'is_email_receipt' => TRUE,
771 'from_email_address' => 'test@test.com',
772 ], CRM_Core_Action
::ADD
);
773 $contributionProduct = $this->callAPISuccess('contribution_product', 'getsingle', []);
774 $this->assertEquals('clumsy smurf', $contributionProduct['product_option']);
776 'Premium Information',
784 * Test functions involving premiums.
786 public function testPremiumUpdateCreditCard() {
787 $form = new CRM_Contribute_Form_Contribution();
788 $mut = new CiviMailUtils($this, TRUE);
790 'total_amount' => 50,
791 'financial_type_id' => 1,
792 'contact_id' => $this->_individualId
,
793 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
794 'contribution_status_id' => 1,
795 'product_name' => [$this->products
[0]['id'], 1],
796 'fulfilled_date' => '',
797 'is_email_receipt' => TRUE,
798 'from_email_address' => 'test@test.com',
799 'payment_processor_id' => $this->paymentProcessorID
,
800 'credit_card_exp_date' => ['M' => 5, 'Y' => 2026],
801 'credit_card_number' => '411111111111111',
802 ], CRM_Core_Action
::ADD
, 'live');
803 $contributionProduct = $this->callAPISuccess('contribution_product', 'getsingle', []);
804 $this->assertEquals('clumsy smurf', $contributionProduct['product_option']);
806 'Premium Information',
814 * Test submitting the back office contribution form with pcp data.
816 * @throws \CRM_Core_Exception
817 * @throws \CiviCRM_API3_Exception
818 * @throws \Civi\Payment\Exception\PaymentProcessorException
820 public function testSubmitWithPCP(): void
{
821 $mut = new CiviMailUtils($this, TRUE);
822 $mut->clearMessages();
823 $params = $this->pcpParams();
824 $pcpID = $this->createPCPBlock($params);
825 $form = new CRM_Contribute_Form_Contribution();
827 'financial_type_id' => 3,
828 'contact_id' => $this->_individualId
,
829 'payment_instrument_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
830 'contribution_status_id' => 1,
832 'pcp_made_through_id' => $pcpID,
833 'pcp_display_in_roll' => '1',
834 'pcp_roll_nickname' => 'Dobby',
835 'pcp_personal_note' => 'I wuz here',
836 ], CRM_Core_Action
::ADD
);
837 $softCredit = $this->callAPISuccessGetSingle('ContributionSoft', []);
838 $this->assertEquals('Dobby', $softCredit['pcp_roll_nickname']);
839 $mut->checkMailLog(['Personal Campaign Page Owner Notification']);
843 * Test the submit function on the contribution page.
845 public function testSubmitWithNote() {
846 $form = new CRM_Contribute_Form_Contribution();
848 'total_amount' => 50,
849 'financial_type_id' => 1,
850 'contact_id' => $this->_individualId
,
851 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
852 'contribution_status_id' => 1,
853 'note' => 'Super cool and interesting stuff',
854 ], CRM_Core_Action
::ADD
);
855 $this->callAPISuccessGetCount('Contribution', ['contact_id' => $this->_individualId
], 1);
856 $note = $this->callAPISuccessGetSingle('note', ['entity_table' => 'civicrm_contribution']);
857 $this->assertEquals($note['note'], 'Super cool and interesting stuff');
861 * Test the submit function on the contribution page.
863 public function testSubmitWithNoteCreditCard() {
864 $form = new CRM_Contribute_Form_Contribution();
867 'total_amount' => 50,
868 'financial_type_id' => 1,
869 'contact_id' => $this->_individualId
,
870 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
871 'contribution_status_id' => 1,
872 'note' => 'Super cool and interesting stuff',
873 ] +
$this->getCreditCardParams(),
874 CRM_Core_Action
::ADD
);
875 $this->callAPISuccessGetCount('Contribution', ['contact_id' => $this->_individualId
], 1);
876 $note = $this->callAPISuccessGetSingle('note', ['entity_table' => 'civicrm_contribution']);
877 $this->assertEquals($note['note'], 'Super cool and interesting stuff');
881 * Test that if a negative contribution is entered it does not get reset to $0.
883 * Note that this fails locally for me & I believe there may be an issue for some sites
884 * with negative numbers. Grep for CRM-16460 to find the places I think that might
885 * be affected if you hit this.
887 public function testEnterNegativeContribution() {
888 $form = new CRM_Contribute_Form_Contribution();
890 'total_amount' => -5,
891 'financial_type_id' => 1,
892 'contact_id' => $this->_individualId
,
893 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
894 'contribution_status_id' => 1,
896 CRM_Core_Action
::ADD
);
897 $this->callAPISuccessGetCount('Contribution', ['contact_id' => $this->_individualId
], 1);
899 $contribution = $this->callAPISuccessGetSingle('Contribution', [
900 'contact_id' => $this->_individualId
,
902 $this->assertEquals(-5, $contribution['total_amount']);
906 * Test the submit function on the contribution page.
908 * @param string $thousandSeparator
910 * @dataProvider getThousandSeparators
912 public function testSubmitUpdate($thousandSeparator) {
913 $this->setCurrencySeparators($thousandSeparator);
914 $form = new CRM_Contribute_Form_Contribution();
917 'total_amount' => $this->formatMoneyInput(6100.10),
918 'financial_type_id' => 1,
919 'contact_id' => $this->_individualId
,
920 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
921 'contribution_status_id' => 1,
923 ], CRM_Core_Action
::ADD
);
924 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId
]);
926 'total_amount' => $this->formatMoneyInput(5200.20),
927 'net_amount' => $this->formatMoneyInput(5200.20),
928 'financial_type_id' => 1,
929 'contact_id' => $this->_individualId
,
930 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
931 'contribution_status_id' => 1,
933 'id' => $contribution['id'],
934 ], CRM_Core_Action
::UPDATE
);
935 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId
]);
936 $this->assertEquals(5200.20, $contribution['total_amount'], 2);
938 $financialTransactions = $this->callAPISuccess('FinancialTrxn', 'get', ['sequential' => TRUE]);
939 $this->assertEquals(2, $financialTransactions['count']);
940 $this->assertEquals(6100.10, $financialTransactions['values'][0]['total_amount']);
941 $this->assertEquals(-899.90, $financialTransactions['values'][1]['total_amount']);
942 $this->assertEquals(-899.90, $financialTransactions['values'][1]['net_amount']);
943 $lineItem = $this->callAPISuccessGetSingle('LineItem', []);
944 $this->assertEquals(5200.20, $lineItem['line_total']);
948 * Test the submit function if only payment instrument is changed from 'Check' to 'Credit Card'
950 * @param string $thousandSeparator
952 * @dataProvider getThousandSeparators
954 public function testSubmitUpdateChangePaymentInstrument($thousandSeparator) {
955 $this->setCurrencySeparators($thousandSeparator);
956 $form = new CRM_Contribute_Form_Contribution();
959 'total_amount' => 1200.55,
960 'financial_type_id' => 1,
961 'contact_id' => $this->_individualId
,
962 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
963 'check_number' => '123AX',
964 'contribution_status_id' => 1,
966 ], CRM_Core_Action
::ADD
);
967 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId
]);
969 'total_amount' => 1200.55,
970 'net_amount' => 1200.55,
971 'financial_type_id' => 1,
972 'contact_id' => $this->_individualId
,
973 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments
),
974 'card_type_id' => CRM_Core_PseudoConstant
::getKey('CRM_Financial_DAO_FinancialTrxn', 'card_type_id', 'Visa'),
975 'pan_truncation' => '1011',
976 'contribution_status_id' => 1,
978 'id' => $contribution['id'],
979 ], CRM_Core_Action
::UPDATE
);
980 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId
]);
981 $this->assertEquals(1200.55, $contribution['total_amount']);
983 $financialTransactions = $this->callAPISuccess('FinancialTrxn', 'get', ['sequential' => TRUE]);
984 $this->assertEquals(3, $financialTransactions['count']);
986 list($oldTrxn, $reversedTrxn, $latestTrxn) = $financialTransactions['values'];
988 $this->assertEquals(1200.55, $oldTrxn['total_amount']);
989 $this->assertEquals('123AX', $oldTrxn['check_number']);
990 $this->assertEquals(array_search('Check', $this->paymentInstruments
), $oldTrxn['payment_instrument_id']);
992 $this->assertEquals(-1200.55, $reversedTrxn['total_amount']);
993 $this->assertEquals('123AX', $reversedTrxn['check_number']);
994 $this->assertEquals(array_search('Check', $this->paymentInstruments
), $reversedTrxn['payment_instrument_id']);
996 $this->assertEquals(1200.55, $latestTrxn['total_amount']);
997 $this->assertEquals('1011', $latestTrxn['pan_truncation']);
998 $this->assertEquals(array_search('Credit Card', $this->paymentInstruments
), $latestTrxn['payment_instrument_id']);
999 $lineItem = $this->callAPISuccessGetSingle('LineItem', []);
1003 * Get parameters for credit card submit calls.
1006 * Credit card specific parameters.
1008 protected function getCreditCardParams() {
1010 'payment_processor_id' => $this->paymentProcessorID
,
1011 'credit_card_exp_date' => ['M' => 5, 'Y' => 2012],
1012 'credit_card_number' => '411111111111111',
1017 * Test the submit function that completes the partially paid payment using Credit Card
1019 * @throws \CRM_Core_Exception
1020 * @throws \CiviCRM_API3_Exception
1022 public function testPartialPaymentWithCreditCard() {
1023 // create a partially paid contribution by using back-office form
1024 $form = new CRM_Contribute_Form_Contribution();
1027 'total_amount' => 50,
1028 'financial_type_id' => 1,
1029 'contact_id' => $this->_individualId
,
1030 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
1031 'check_number' => substr(sha1(rand()), 0, 7),
1032 'billing_city-5' => 'Vancouver',
1033 'contribution_status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'),
1034 ], CRM_Core_Action
::ADD
1037 $contribution = $this->callAPISuccessGetSingle('Contribution', []);
1038 $this->callAPISuccess('Payment', 'create', ['contribution_id' => $contribution['id'], 'total_amount' => 10, 'payment_instrument_id' => 'Cash']);
1039 $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $contribution['id']]);
1040 $this->assertEquals('Partially paid', $contribution['contribution_status']);
1041 // pay additional amount by using Credit Card
1042 $form = new CRM_Contribute_Form_AdditionalPayment();
1044 'contribution_id' => $contribution['id'],
1045 'contact_id' => $this->_individualId
,
1046 'total_amount' => 40,
1047 'currency' => 'USD',
1048 'financial_type_id' => 1,
1049 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments
),
1050 'payment_processor_id' => $this->paymentProcessorID
,
1051 'credit_card_exp_date' => ['M' => 5, 'Y' => 2025],
1052 'credit_card_number' => '411111111111111',
1054 'credit_card_type' => 'Visa',
1055 'billing_city-5' => 'Vancouver',
1056 'billing_state_province_id-5' => 1059,
1057 'billing_postal_code-5' => 1321312,
1058 'billing_country_id-5' => 1228,
1059 'trxn_date' => '2017-04-11 13:05:11',
1061 $contribution = $this->callAPISuccessGetSingle('Contribution', []);
1062 $this->assertNotEmpty($contribution);
1063 $this->assertEquals('Completed', $contribution['contribution_status']);
1067 * Test the submit function for FT with tax.
1069 * @param string $thousandSeparator
1071 * @dataProvider getThousandSeparators
1073 public function testSubmitSaleTax($thousandSeparator) {
1074 $this->setCurrencySeparators($thousandSeparator);
1075 $this->enableTaxAndInvoicing();
1076 $this->addTaxAccountToFinancialType($this->_financialTypeId
);
1077 $form = new CRM_Contribute_Form_Contribution();
1080 'total_amount' => $this->formatMoneyInput(1000.00),
1081 'financial_type_id' => $this->_financialTypeId
,
1082 'contact_id' => $this->_individualId
,
1083 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
1084 'contribution_status_id' => 1,
1085 'price_set_id' => 0,
1087 CRM_Core_Action
::ADD
1089 $contribution = $this->callAPISuccessGetSingle('Contribution',
1091 'contact_id' => $this->_individualId
,
1092 'return' => ['tax_amount', 'total_amount'],
1095 $this->assertEquals(1100, $contribution['total_amount']);
1096 $this->assertEquals(100, $contribution['tax_amount']);
1097 $this->callAPISuccessGetCount('FinancialTrxn', [], 1);
1098 $this->callAPISuccessGetCount('FinancialItem', [], 2);
1099 $lineItem = $this->callAPISuccessGetSingle('LineItem', ['contribution_id' => $contribution['id']]);
1100 $this->assertEquals(1000, $lineItem['line_total']);
1101 $this->assertEquals(100, $lineItem['tax_amount']);
1103 // CRM-20423: Upon simple submit of 'Edit Contribution' form ensure that total amount is same
1105 'id' => $contribution['id'],
1106 'financial_type_id' => 3,
1107 'contact_id' => $this->_individualId
,
1108 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
1109 'contribution_status_id' => 1,
1110 ], CRM_Core_Action
::UPDATE
);
1112 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId
]);
1113 // Check if total amount is unchanged
1114 $this->assertEquals(1100, $contribution['total_amount']);
1118 * Test the submit function for FT without tax.
1120 * @throws \CRM_Core_Exception
1121 * @throws \CiviCRM_API3_Exception
1122 * @throws \Civi\Payment\Exception\PaymentProcessorException
1124 public function testSubmitWithOutSaleTax() {
1125 $this->enableTaxAndInvoicing();
1126 $this->addTaxAccountToFinancialType($this->_financialTypeId
);
1127 $form = new CRM_Contribute_Form_Contribution();
1130 'total_amount' => 100,
1131 'financial_type_id' => 3,
1132 'contact_id' => $this->_individualId
,
1133 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
1134 'contribution_status_id' => 1,
1135 'price_set_id' => 0,
1136 ], CRM_Core_Action
::ADD
);
1137 $contribution = $this->callAPISuccessGetSingle('Contribution',
1139 'contact_id' => $this->_individualId
,
1140 'return' => ['tax_amount', 'total_amount'],
1143 $this->assertEquals(100, $contribution['total_amount']);
1144 $this->assertEquals(NULL, $contribution['tax_amount']);
1145 $this->callAPISuccessGetCount('FinancialTrxn', [], 1);
1146 $this->callAPISuccessGetCount('FinancialItem', [], 1);
1147 $lineItem = $this->callAPISuccessGetSingle('LineItem', [
1148 'contribution_id' => $contribution['id'],
1149 'return' => ['line_total', 'tax_amount'],
1151 $this->assertEquals(100, $lineItem['line_total']);
1152 $this->assertEquals(0.00, $lineItem['tax_amount']);
1156 * Create a contribution & then edit it via backoffice form, checking tax with: default price_set
1158 * @param string $thousandSeparator
1160 * @dataProvider getThousandSeparators
1162 * @throws \Exception
1164 public function testReSubmitSaleTax($thousandSeparator): void
{
1165 $this->setCurrencySeparators($thousandSeparator);
1166 $this->enableTaxAndInvoicing();
1167 $this->addTaxAccountToFinancialType($this->_financialTypeId
);
1168 [$form, $contribution] = $this->doInitialSubmit();
1169 $this->assertEquals(11000, $contribution['total_amount']);
1170 $this->assertEquals(1000, $contribution['tax_amount']);
1171 $this->assertEquals(11000, $contribution['net_amount']);
1173 $mut = new CiviMailUtils($this, TRUE);
1174 // Testing here if when we edit something trivial like adding a check_number tax, net, total amount stay the same:
1176 'id' => $contribution['id'],
1177 'tax_amount' => $contribution['tax_amount'],
1178 'financial_type_id' => $contribution['financial_type_id'],
1179 'receive_date' => $contribution['receive_date'],
1180 'payment_instrument_id' => $contribution['payment_instrument_id'],
1181 'price_set_id' => 0,
1182 'check_number' => 12345,
1183 'contribution_status_id' => 1,
1184 'is_email_receipt' => 1,
1185 'from_email_address' => 'demo@example.com',
1186 ], CRM_Core_Action
::UPDATE
);
1187 $contribution = $this->callAPISuccessGetSingle('Contribution',
1189 'contribution_id' => 1,
1190 'return' => ['tax_amount', 'total_amount', 'net_amount', 'financial_type_id', 'receive_date', 'payment_instrument_id'],
1193 $this->assertEquals(11000, $contribution['total_amount']);
1194 $this->assertEquals(1000, $contribution['tax_amount']);
1195 $this->assertEquals(11000, $contribution['net_amount']);
1198 'Total Tax Amount : $ ' . $this->formatMoneyInput(1000.00),
1199 'Total Amount : $ ' . $this->formatMoneyInput(11000.00),
1200 'Date Received: April 21st, 2015',
1202 'Check Number: 12345',
1205 $mut->checkMailLog($strings);
1206 $this->callAPISuccessGetCount('FinancialTrxn', [], 3);
1207 $items = $this->callAPISuccess('FinancialItem', 'get', ['sequential' => 1])['values'];
1208 $this->assertCount(2, $items);
1209 $this->assertEquals('Contribution Amount', $items[0]['description']);
1210 $this->assertEquals('Sales Tax', $items[1]['description']);
1212 $this->assertEquals(10000, $items[0]['amount']);
1213 $this->assertEquals(1000, $items[1]['amount']);
1217 * Create a contribution & then edit it via backoffice form, checking tax with: default price_set
1219 * @param string $thousandSeparator
1221 * @dataProvider getThousandSeparators
1223 * @throws \Exception
1225 public function testReSubmitSaleTaxAlteredAmount($thousandSeparator) {
1226 $this->setCurrencySeparators($thousandSeparator);
1227 $this->enableTaxAndInvoicing();
1228 $this->addTaxAccountToFinancialType($this->_financialTypeId
);
1229 [$form, $contribution] = $this->doInitialSubmit();
1231 $mut = new CiviMailUtils($this, TRUE);
1232 // Testing here if when we edit something trivial like adding a check_number tax, net, total amount stay the same:
1234 'id' => $contribution['id'],
1235 'total_amount' => $this->formatMoneyInput(20000),
1236 'tax_amount' => $this->formatMoneyInput(2000),
1237 'financial_type_id' => $contribution['financial_type_id'],
1238 'receive_date' => $contribution['receive_date'],
1239 'payment_instrument_id' => $contribution['payment_instrument_id'],
1240 'price_set_id' => 0,
1241 'check_number' => 12345,
1242 'contribution_status_id' => 1,
1243 'is_email_receipt' => 1,
1244 'from_email_address' => 'demo@example.com',
1245 ], CRM_Core_Action
::UPDATE
);
1246 $contribution = $this->callAPISuccessGetSingle('Contribution',
1248 'contribution_id' => 1,
1249 'return' => ['tax_amount', 'total_amount', 'net_amount', 'financial_type_id', 'receive_date', 'payment_instrument_id'],
1252 $this->assertEquals(22000, $contribution['total_amount']);
1253 $this->assertEquals(2000, $contribution['tax_amount']);
1254 $this->assertEquals(22000, $contribution['net_amount']);
1257 'Total Tax Amount : $ ' . $this->formatMoneyInput(2000),
1258 'Total Amount : $ ' . $this->formatMoneyInput(22000.00),
1259 'Date Received: April 21st, 2015',
1261 'Check Number: 12345',
1264 $mut->checkMailLog($strings);
1265 $this->callAPISuccessGetCount('FinancialTrxn', [], 4);
1266 $items = $this->callAPISuccess('FinancialItem', 'get', ['sequential' => 1]);
1267 $this->assertEquals(4, $items['count']);
1268 $this->assertEquals('Contribution Amount', $items['values'][0]['description']);
1269 $this->assertEquals('Sales Tax', $items['values'][1]['description']);
1270 $this->assertEquals('Contribution Amount', $items['values'][0]['description']);
1271 $this->assertEquals('Sales Tax', $items['values'][1]['description']);
1273 $this->assertEquals(10000, $items['values'][0]['amount']);
1274 $this->assertEquals(1000, $items['values'][1]['amount']);
1275 $this->assertEquals(10000, $items['values'][2]['amount']);
1276 $this->assertEquals(1000, $items['values'][3]['amount']);
1280 * Do the first contributions, in preparation for an edit-submit.
1284 * @throws \Exception
1286 protected function doInitialSubmit() {
1287 $form = new CRM_Contribute_Form_Contribution();
1290 'total_amount' => $this->formatMoneyInput(10000),
1291 'financial_type_id' => $this->_financialTypeId
,
1292 'receive_date' => '2015-04-21 00:00:00',
1293 'contact_id' => $this->_individualId
,
1294 'payment_instrument_id' => array_search('Check', $this->paymentInstruments
),
1295 'contribution_status_id' => 1,
1296 'price_set_id' => 0,
1297 ], CRM_Core_Action
::ADD
);
1298 $contribution = $this->callAPISuccessGetSingle('Contribution',
1300 'contribution_id' => 1,
1305 'financial_type_id',
1307 'payment_instrument_id',
1311 $this->assertEquals(11000, $contribution['total_amount']);
1312 $this->assertEquals(1000, $contribution['tax_amount']);
1313 $this->assertEquals(11000, $contribution['net_amount']);
1314 return [$form, $contribution];
1318 * function to test card_type and pan truncation.
1320 public function testCardTypeAndPanTruncation() {
1321 $form = new CRM_Contribute_Form_Contribution();
1324 'total_amount' => 100,
1325 'financial_type_id' => 3,
1326 'contact_id' => $this->_individualId
,
1327 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments
),
1328 'contribution_status_id' => 1,
1329 'credit_card_type' => 'Visa',
1330 'pan_truncation' => 4567,
1331 'price_set_id' => 0,
1333 CRM_Core_Action
::ADD
1335 $contribution = $this->callAPISuccessGetSingle('Contribution',
1337 'contact_id' => $this->_individualId
,
1341 $lastFinancialTrxnId = CRM_Core_BAO_FinancialTrxn
::getFinancialTrxnId($contribution['id'], 'DESC');
1342 $financialTrxn = $this->callAPISuccessGetSingle(
1345 'id' => $lastFinancialTrxnId['financialTrxnId'],
1346 'return' => ['card_type_id.label', 'pan_truncation'],
1349 $this->assertEquals(CRM_Utils_Array
::value('card_type_id.label', $financialTrxn), 'Visa');
1350 $this->assertEquals(CRM_Utils_Array
::value('pan_truncation', $financialTrxn), 4567);
1354 * Check payment processor is correctly assigned for a contribution page.
1356 * @throws \CRM_Core_Exception
1357 * @throws \CRM_Contribute_Exception_InactiveContributionPageException
1359 public function testContributionBasePreProcess() {
1360 //Create contribution page with only pay later enabled.
1362 'title' => "Test Contribution Page",
1363 'financial_type_id' => 1,
1364 'currency' => 'NZD',
1365 'goal_amount' => 100,
1366 'is_pay_later' => 1,
1367 'pay_later_text' => 'Send check',
1368 'is_monetary' => TRUE,
1369 'is_active' => TRUE,
1370 'is_email_receipt' => TRUE,
1371 'receipt_from_email' => 'yourconscience@donate.com',
1372 'receipt_from_name' => 'Ego Freud',
1375 $page1 = $this->callAPISuccess("contribution_page", 'create', $params);
1377 //Execute CRM_Contribute_Form_ContributionBase preProcess
1378 //and check the assignment of payment processors
1379 $form = new CRM_Contribute_Form_ContributionBase();
1380 $form->controller
= new CRM_Core_Controller();
1381 $form->set('id', $page1['id']);
1382 $_REQUEST['id'] = $page1['id'];
1384 $form->preProcess();
1385 $this->assertEquals($form->_paymentProcessor
['name'], 'pay_later');
1387 //Disable all the payment processor for the contribution page.
1388 $params['is_pay_later'] = 0;
1389 $page2 = $this->callAPISuccess("contribution_page", 'create', $params);
1391 //Assert an exception is thrown on loading the contribution page.
1392 $form = new CRM_Contribute_Form_ContributionBase();
1393 $form->controller
= new CRM_Core_Controller();
1394 $_REQUEST['id'] = $page2['id'];
1395 $form->set('id', $page2['id']);
1397 $form->preProcess();
1399 catch (CRM_Core_Exception
$e) {
1400 $this->assertContains("A payment processor configured for this page might be disabled (contact the site administrator for assistance).", $e->getMessage());
1403 $this->fail('Exception was expected');
1407 * function to test card_type and pan truncation.
1409 public function testCardTypeAndPanTruncationLiveMode() {
1410 $visaID = CRM_Core_PseudoConstant
::getKey('CRM_Core_BAO_FinancialTrxn', 'card_type_id', 'Visa');
1411 $form = new CRM_Contribute_Form_Contribution();
1412 $form->_mode
= 'Live';
1415 'total_amount' => 50,
1416 'financial_type_id' => 1,
1417 'contact_id' => $this->_individualId
,
1418 'credit_card_number' => 4444333322221111,
1419 'payment_instrument_id' => array_search('Credit Card', $this->paymentInstruments
),
1421 'credit_card_exp_date' => [
1423 'Y' => date('Y', strtotime('+5 years')),
1425 'credit_card_type' => 'Visa',
1426 'billing_first_name' => 'Junko',
1427 'billing_middle_name' => '',
1428 'billing_last_name' => 'Adams',
1429 'billing_street_address-5' => '790L Lincoln St S',
1430 'billing_city-5' => 'Maryknoll',
1431 'billing_state_province_id-5' => 1031,
1432 'billing_postal_code-5' => 10545,
1433 'billing_country_id-5' => 1228,
1434 'frequency_interval' => 1,
1435 'frequency_unit' => 'month',
1436 'installments' => '',
1437 'hidden_AdditionalDetail' => 1,
1438 'hidden_Premium' => 1,
1439 'from_email_address' => '"civi45" <civi45@civicrm.com>',
1440 'receipt_date' => '',
1441 'receipt_date_time' => '',
1442 'payment_processor_id' => $this->paymentProcessorID
,
1443 'currency' => 'USD',
1444 'source' => 'bob sled race',
1446 CRM_Core_Action
::ADD
1448 $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId
]);
1449 $lastFinancialTrxnId = CRM_Core_BAO_FinancialTrxn
::getFinancialTrxnId($contribution['id'], 'DESC');
1450 $financialTrxn = $this->callAPISuccessGetSingle(
1453 'id' => $lastFinancialTrxnId['financialTrxnId'],
1454 'return' => ['card_type_id', 'pan_truncation'],
1457 $this->assertEquals($visaID, $financialTrxn['card_type_id']);
1458 $this->assertEquals(1111, $financialTrxn['pan_truncation']);
1462 * CRM-21711 Test that custom fields on relevant memberships get updated wehn updating multiple memberships
1464 public function testCustomFieldsOnMembershipGetUpdated() {
1465 $contactID = $this->individualCreate();
1466 $contactID1 = $this->organizationCreate();
1467 $contactID2 = $this->organizationCreate();
1469 // create membership types
1470 $membershipTypeOne = civicrm_api3('membership_type', 'create', [
1473 'member_of_contact_id' => $contactID1,
1474 'duration_unit' => "year",
1475 'minimum_fee' => 50,
1476 'duration_interval' => 1,
1477 'period_type' => "fixed",
1478 'fixed_period_start_day' => "101",
1479 'fixed_period_rollover_day' => "1231",
1480 'financial_type_id' => 1,
1483 'visibility' => "Public",
1486 $membershipTypeTwo = civicrm_api3('membership_type', 'create', [
1489 'member_of_contact_id' => $contactID2,
1490 'duration_unit' => "year",
1491 'minimum_fee' => 50,
1492 'duration_interval' => 1,
1493 'period_type' => "fixed",
1494 'fixed_period_start_day' => "101",
1495 'fixed_period_rollover_day' => "1231",
1496 'financial_type_id' => 1,
1499 'visibility' => "Public",
1502 //create custom Fields
1503 $membershipCustomFieldsGroup = civicrm_api3('CustomGroup', 'create', [
1504 'title' => "Custom Fields on Membership",
1505 'extends' => "Membership",
1508 $membershipCustomField = civicrm_api3('CustomField', 'create', [
1509 "custom_group_id" => $membershipCustomFieldsGroup['id'],
1510 "name" => "my_membership_custom_field",
1511 "label" => "Membership Custom Field",
1512 "data_type" => "String",
1513 "html_type" => "Text",
1516 "text_length" => "255",
1520 $membershipCustomFieldsProfile = civicrm_api3('UFGroup', 'create', [
1522 "group_type" => "Membership,Individual",
1523 "title" => "Membership Custom Fields",
1524 "add_captcha" => "0",
1526 "is_edit_link" => "0",
1527 "is_uf_link" => "0",
1528 "is_update_dupe" => "0",
1531 // add custom fields to profile
1532 $membershipCustomFieldsProfileFields = civicrm_api3('UFField', 'create', [
1533 "uf_group_id" => $membershipCustomFieldsProfile['id'],
1534 "field_name" => "custom_" . $membershipCustomField['id'],
1536 "visibility" => "User and User Admin Only",
1537 "in_selector" => "0",
1538 "is_searchable" => "0",
1539 "label" => "custom text field on membership",
1540 "field_type" => "Membership",
1543 $contribPage = civicrm_api3('ContributionPage', 'create', [
1544 "title" => "Membership",
1545 "financial_type_id" => 1,
1546 'financial_account_id' => 1,
1547 "is_credit_card_only" => "0",
1548 "is_monetary" => "0",
1550 "is_confirm_enabled" => "1",
1551 "is_recur_interval" => "0",
1552 "is_recur_installments" => "0",
1553 "adjust_recur_start_date" => "0",
1554 "is_pay_later" => "1",
1555 "pay_later_text" => "I will send payment by check",
1556 "is_partial_payment" => "0",
1557 "is_allow_other_amount" => "0",
1558 "is_email_receipt" => "0",
1560 "amount_block_is_active" => "0",
1561 "currency" => "USD",
1563 "is_billing_required" => "0",
1564 "contribution_type_id" => "2",
1565 'is_allow_other_amount' => 1,
1567 'max_amount' => 1000,
1569 $contribPage1 = $contribPage['id'];
1571 //create price set with two options for the two different memberships
1572 $priceSet = civicrm_api3('PriceSet', 'create', [
1573 'title' => "Two Membership Type Checkbox",
1574 'extends' => "CiviMember",
1576 "financial_type_id" => "1",
1577 "is_quick_config" => "0",
1578 "is_reserved" => "0",
1579 "entity" => ["civicrm_contribution_page" => [$contribPage1]],
1582 $priceField = civicrm_api3('PriceField', 'create', [
1583 "price_set_id" => $priceSet['id'],
1585 "label" => "Membership Types",
1586 "html_type" => "CheckBox",
1587 "is_enter_qty" => "0",
1589 "is_display_amounts" => "1",
1590 "options_per_line" => "1",
1592 "is_required" => "0",
1593 "visibility_id" => "1",
1596 $priceFieldOption1 = civicrm_api3('PriceFieldValue', 'create', [
1597 "price_field_id" => $priceField['id'],
1598 "name" => "membership_type_one",
1599 "label" => "Membership Type One",
1602 "membership_type_id" => $membershipTypeOne['id'],
1603 "membership_num_terms" => "1",
1604 "is_default" => "0",
1606 "financial_type_id" => "1",
1607 "non_deductible_amount" => "0.00",
1608 "contribution_type_id" => "2",
1611 $priceFieldOption2 = civicrm_api3('PriceFieldValue', 'create', [
1612 "price_field_id" => $priceField['id'],
1613 "name" => "membership_type_two",
1614 "label" => "Membership Type Two",
1617 "membership_type_id" => $membershipTypeTwo['id'],
1618 "membership_num_terms" => "1",
1619 "is_default" => "0",
1621 "financial_type_id" => "1",
1622 "non_deductible_amount" => "0.00",
1623 "contribution_type_id" => "2",
1626 // assign profile with custom fields to contribution page
1627 $profile = civicrm_api3('UFJoin', 'create', [
1628 'module' => "CiviContribute",
1630 'uf_group_id' => $membershipCustomFieldsProfile['id'],
1631 "entity_table" => "civicrm_contribution_page",
1632 "entity_id" => $contribPage1,
1635 $form = new CRM_Contribute_Form_Contribution_Confirm();
1637 'id' => $contribPage1,
1638 "qfKey" => "donotcare",
1639 "custom_{$membershipCustomField['id']}" => "Hello",
1640 "email-5" => "admin@example.com",
1641 "priceSetId" => $priceSet['id'],
1642 'price_set_id' => $priceSet['id'],
1643 "price_" . $priceField['id'] => [$priceFieldOption1['id'] => 1, $priceFieldOption2['id'] => 1],
1644 "invoiceID" => "9a6f7b49358dc31c3604e463b225c5be",
1645 "email" => "admin@example.com",
1646 "currencyID" => "USD",
1647 'description' => "Membership Contribution",
1648 'contact_id' => $contactID,
1649 'skipLineItem' => 0,
1650 'email-5' => 'test@test.com',
1652 'tax_amount' => 0.00,
1653 'is_pay_later' => 1,
1654 'is_quick_config' => 1,
1656 $form->submit($form->_params
);
1657 $membership1 = civicrm_api3('Membership', 'getsingle', [
1658 'contact_id' => $contactID,
1659 'membership_type_id' => $membershipTypeOne['id'],
1661 $this->assertEquals("Hello", $membership1["custom_{$membershipCustomField['id']}"]);
1663 $membership2 = civicrm_api3('Membership', 'getsingle', [
1664 'contact_id' => $contactID,
1665 'membership_type_id' => $membershipTypeTwo['id'],
1667 $this->assertEquals("Hello", $membership2["custom_{$membershipCustomField['id']}"]);
1671 * Test non-membership donation on a contribution page
1672 * using membership priceset.
1674 public function testDonationOnMembershipPagePriceset() {
1675 $contactID = $this->individualCreate();
1676 $this->createPriceSetWithPage();
1677 $form = new CRM_Contribute_Form_Contribution_Confirm();
1678 $form->controller
= new CRM_Core_Controller();
1680 'id' => $this->_ids
['contribution_page'],
1681 "qfKey" => "donotcare",
1682 "priceSetId" => $this->_ids
['price_set'],
1683 'price_set_id' => $this->_ids
['price_set'],
1684 "price_" . $this->_ids
['price_field'][0] => $this->_ids
['price_field_value']['cont'],
1685 "invoiceID" => "9a6f7b49358dc31c3604e463b225c5be",
1686 "email" => "admin@example.com",
1687 "currencyID" => "USD",
1688 'description' => "Membership Contribution",
1689 'contact_id' => $contactID,
1690 'select_contact_id' => $contactID,
1691 'useForMember' => 1,
1692 'skipLineItem' => 0,
1693 'email-5' => 'test@test.com',
1695 'tax_amount' => NULL,
1696 'is_pay_later' => 1,
1697 'is_quick_config' => 1,
1699 $form->submit($form->_params
);
1701 $contribution = $this->callAPISuccessGetSingle('Contribution', [
1702 'contact_id' => $contactID,
1704 //Check no membership is created.
1705 $this->callAPIFailure('Membership', 'getsingle', [
1706 'contact_id' => $contactID,
1708 $this->contributionDelete($contribution['id']);
1710 //Choose Membership Priceset
1711 $form->_params
["price_{$this->_ids['price_field'][0]}"] = $this->_ids
['price_field_value'][0];
1712 $form->_params
["amount"] = 20;
1713 $form->submit($form->_params
);
1715 $contribution = $this->callAPISuccessGetSingle('Contribution', [
1716 'contact_id' => $contactID,
1718 //Check membership is created for the contact.
1719 $membership = $this->callAPISuccessGetSingle('Membership', [
1720 'contact_id' => $contactID,
1722 $membershipPayment = $this->callAPISuccessGetSingle('MembershipPayment', [
1723 'contribution_id' => $contribution['id'],
1725 $this->assertEquals($membershipPayment['membership_id'], $membership['id']);
1726 $this->membershipDelete($membership['id']);
1730 * Test no warnings or errors during preProcess when editing.
1732 public function testPreProcessContributionEdit() {
1733 // Simulate a contribution in pending status
1734 $contribution = $this->callAPISuccess(
1737 array_merge($this->_params
, ['contribution_status_id' => 'Pending'])
1740 // set up the form to edit the contribution and call preProcess
1741 $form = $this->getFormObject('CRM_Contribute_Form_Contribution');
1742 $_REQUEST['cid'] = $this->_individualId
;
1743 $_REQUEST['id'] = $contribution['id'];
1744 $form->_action
= CRM_Core_Action
::UPDATE
;
1745 $form->preProcess();
1747 // Check something while we're here
1748 $this->assertEquals($contribution['id'], $form->_values
['contribution_id']);
1750 unset($_REQUEST['cid']);
1751 unset($_REQUEST['id']);
1755 * Mostly just check there's no errors opening the Widget tab on contribution
1758 public function testOpeningWidgetAdminPage() {
1759 $page_id = $this->callAPISuccess('ContributionPage', 'create', [
1760 'title' => 'my page',
1761 'financial_type_id' => $this->_financialTypeId
,
1762 'payment_processor' => $this->paymentProcessorID
,
1765 $form = new CRM_Contribute_Form_ContributionPage_Widget();
1766 $form->controller
= new CRM_Core_Controller_Simple('CRM_Contribute_Form_ContributionPage_Widget', 'Widget');
1768 $form->set('reset', '1');
1769 $form->set('action', 'update');
1770 $form->set('id', $page_id);
1773 $form->controller
->_actions
['display']->perform($form, 'display');
1774 $contents = ob_get_contents();
1777 // The page contents load later by ajax, so there's just the surrounding
1778 // html available now, but we can check at least one thing while we're here.
1779 $this->assertContains("mainTabContainer", $contents);
1783 * Test AdditionalInfo::postProcessCommon
1784 * @dataProvider additionalInfoProvider
1785 * @param array $input
1786 * @param array $expectedFormatted
1788 public function testAdditionalInfoPostProcessCommon(array $input, array $expectedFormatted) {
1790 $dummy = new CRM_Contribute_Form_AdditionalInfo();
1791 CRM_Contribute_Form_AdditionalInfo
::postProcessCommon($input, $formatted, $dummy);
1792 $this->assertEquals($expectedFormatted, $formatted);
1796 * Dataprovider for testAdditionalInfoPostProcessCommon
1799 public function additionalInfoProvider(): array {
1803 'qfKey' => 'CRMContributeFormContributionu2pbzqqmz74oscck4ss4osccw4wgccc884wkk4ws0o8wgss4w_8953',
1804 'entryURL' => 'http://example.org/civicrm/contact/view/contribution?reset=1&action=add&cid=1&context=contribution',
1805 'check_number' => '',
1806 'frequency_interval' => '1',
1807 'hidden_AdditionalDetail' => '1',
1808 'contact_id' => '1',
1809 'financial_type_id' => '1',
1810 'payment_instrument_id' => '4',
1812 'from_email_address' => '2',
1813 'contribution_status_id' => '1',
1814 // This is unused here but is iffy to put in a dataprovider
1815 'receive_date' => '2021-01-14 11:12:13',
1816 'receipt_date' => '',
1817 'cancel_date' => '',
1818 'cancel_reason' => '',
1819 'price_set_id' => '',
1820 'total_amount' => 10,
1821 'currency' => 'USD',
1822 'source' => 'a source',
1823 'soft_credit_contact_id' => [
1835 'soft_credit_amount' => [
1847 'soft_credit_type' => [
1859 'sct_default_id' => '3',
1860 'MAX_FILE_SIZE' => '2097152',
1861 'ip_address' => '127.0.0.1',
1868 'non_deductible_amount' => NULL,
1869 'total_amount' => 10,
1870 'fee_amount' => NULL,
1872 'invoice_id' => NULL,
1873 'creditnote_id' => NULL,
1874 'campaign_id' => NULL,
1875 'contribution_page_id' => NULL,
1876 'thankyou_date' => 'null',
1883 'qfKey' => 'CRMContributeFormContributionu2pbzqqmz74oscck4ss4osccw4wgccc884wkk4ws0o8wgss4w_8953',
1884 'entryURL' => 'http://example.org/civicrm/contact/view/contribution?reset=1&action=add&cid=1&context=contribution',
1886 'frequency_interval' => '1',
1887 'hidden_AdditionalDetail' => '1',
1888 'thankyou_date' => '2021-01-14',
1889 'non_deductible_amount' => '0.00',
1890 'fee_amount' => '0.00',
1892 'creditnote_id' => '',
1893 'contribution_page_id' => '',
1895 'contact_id' => '1',
1896 'financial_type_id' => '1',
1897 'from_email_address' => '2',
1898 'contribution_status_id' => '1',
1899 // This is unused here but is iffy to put in a dataprovider
1900 'receive_date' => '2021-01-14 11:12:13',
1901 'receipt_date' => '',
1902 'cancel_date' => '',
1903 'cancel_reason' => '',
1904 'price_set_id' => '',
1905 'total_amount' => '10.00',
1906 'currency' => 'USD',
1907 'source' => 'a source',
1908 'soft_credit_contact_id' => [
1920 'soft_credit_amount' => [
1932 'soft_credit_type' => [
1944 'sct_default_id' => '3',
1945 'MAX_FILE_SIZE' => '2097152',
1946 'ip_address' => '127.0.0.1',
1947 // leaving out since don't want to enforce string 'null' in a test
1948 //'tax_amount' => 'null',
1951 'non_deductible_amount' => '0.00',
1952 'total_amount' => '10.00',
1953 'fee_amount' => '0.00',
1956 'creditnote_id' => '',
1957 'campaign_id' => NULL,
1958 'contribution_page_id' => NULL,
1959 'thankyou_date' => '20210114000000',
1964 'date-and-time' => [
1966 'qfKey' => 'CRMContributeFormContributionu2pbzqqmz74oscck4ss4osccw4wgccc884wkk4ws0o8wgss4w_8953',
1967 'entryURL' => 'http://example.org/civicrm/contact/view/contribution?reset=1&action=add&cid=1&context=contribution',
1969 'frequency_interval' => '1',
1970 'hidden_AdditionalDetail' => '1',
1971 'thankyou_date' => '2021-01-14 10:11:12',
1972 'non_deductible_amount' => '0.00',
1973 'fee_amount' => '0.00',
1975 'creditnote_id' => '',
1976 'contribution_page_id' => '',
1978 'contact_id' => '1',
1979 'financial_type_id' => '1',
1980 'from_email_address' => '2',
1981 'contribution_status_id' => '1',
1982 // This is unused here but is iffy to put in a dataprovider
1983 'receive_date' => '2021-01-14 11:12:13',
1984 'receipt_date' => '',
1985 'cancel_date' => '',
1986 'cancel_reason' => '',
1987 'price_set_id' => '',
1988 'total_amount' => '10.00',
1989 'currency' => 'USD',
1990 'source' => 'a source',
1991 'soft_credit_contact_id' => [
2003 'soft_credit_amount' => [
2015 'soft_credit_type' => [
2027 'sct_default_id' => '3',
2028 'MAX_FILE_SIZE' => '2097152',
2029 'ip_address' => '127.0.0.1',
2030 // leaving out since don't want to enforce string 'null' in a test
2031 //'tax_amount' => 'null',
2034 'non_deductible_amount' => '0.00',
2035 'total_amount' => '10.00',
2036 'fee_amount' => '0.00',
2039 'creditnote_id' => '',
2040 'campaign_id' => NULL,
2041 'contribution_page_id' => NULL,
2042 'thankyou_date' => '20210114101112',
2052 public function testContributionFormRule() {
2054 'contact_id' => $this->_individualId
,
2055 'financial_type_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Donation'),
2056 'currency' => 'USD',
2057 'total_amount' => '10',
2058 'price_set_id' => '',
2060 'contribution_status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
2061 'cancel_date' => '',
2062 'cancel_reason' => '',
2063 'receive_date' => date('Y-m-d H:i:s'),
2064 'from_email_address' => key(CRM_Core_BAO_Email
::getFromEmail()),
2065 'receipt_date' => '',
2066 'payment_instrument_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
2068 'check_number' => '',
2069 'soft_credit_contact_id' => [
2081 'soft_credit_amount' => [
2093 'soft_credit_type' => [
2107 $form = new CRM_Contribute_Form_Contribution();
2108 $this->assertSame([], $form->formRule($fields, [], $form));
2112 * Check that formRule validates you can only have one contribution with a
2115 public function testContributionFormRuleDuplicateTrxn() {
2116 $contribution = $this->callAPISuccess('Contribution', 'create', array_merge($this->_params
, ['trxn_id' => '1234']));
2119 'contact_id' => $this->_individualId
,
2120 'financial_type_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Donation'),
2121 'currency' => 'USD',
2122 'total_amount' => '10',
2123 'price_set_id' => '',
2125 'contribution_status_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
2126 'cancel_date' => '',
2127 'cancel_reason' => '',
2128 'receive_date' => date('Y-m-d H:i:s'),
2129 'from_email_address' => key(CRM_Core_BAO_Email
::getFromEmail()),
2130 'receipt_date' => '',
2131 'payment_instrument_id' => CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
2132 'trxn_id' => '1234',
2133 'check_number' => '',
2134 'soft_credit_contact_id' => [
2146 'soft_credit_amount' => [
2158 'soft_credit_type' => [
2172 $form = new CRM_Contribute_Form_Contribution();
2173 $this->assertEquals(['trxn_id' => "Transaction ID's must be unique. Transaction '1234' already exists in your database."], $form->formRule($fields, [], $form));