Fix empty money handling
[civicrm-core.git] / tests / phpunit / CRM / Event / Form / ParticipantTest.php
1 <?php
2
3 use Civi\Api4\Participant;
4
5 /**
6 * Test CRM_Event_Form_Registration functions.
7 *
8 * @package CiviCRM
9 * @group headless
10 */
11 class CRM_Event_Form_ParticipantTest extends CiviUnitTestCase {
12
13 use CRMTraits_Financial_OrderTrait;
14 /**
15 * Options on the from Email address array.
16 *
17 * @var array
18 */
19 protected $fromEmailAddressOptions = [];
20
21 public function setUp(): void {
22 $this->useTransaction(TRUE);
23 parent::setUp();
24 }
25
26 /**
27 * Initial test of submit function.
28 *
29 * @throws \Exception
30 */
31 public function testSubmit(): void {
32 $form = $this->getForm();
33 $form->submit([
34 'register_date' => date('Ymd'),
35 'status_id' => 1,
36 'role_id' => 1,
37 'event_id' => $form->_eventId,
38 ]);
39 $this->callAPISuccessGetSingle('Participant', []);
40 }
41
42 /**
43 * Test financial items pending transaction is later altered.
44 *
45 * @throws \Exception
46 */
47 public function testSubmitUnpaidPriceChangeWhileStillPending() {
48 $form = $this->getForm(['is_monetary' => 1, 'financial_type_id' => 1]);
49 $form->_quickConfig = TRUE;
50
51 $form->_lineItem = [
52 0 => [
53 13 => [
54 'price_field_id' => $this->getPriceFieldID(),
55 'price_field_value_id' => $this->_ids['price_field_value'][0],
56 'label' => 'Tiny-tots (ages 5-8)',
57 'field_title' => 'Tournament Fees',
58 'description' => NULL,
59 'qty' => 1,
60 'unit_price' => '800.000000000',
61 'line_total' => 800.0,
62 'participant_count' => 0,
63 'max_value' => NULL,
64 'membership_type_id' => NULL,
65 'membership_num_terms' => NULL,
66 'auto_renew' => NULL,
67 'html_type' => 'Radio',
68 'financial_type_id' => '4',
69 'tax_amount' => NULL,
70 'non_deductible_amount' => '0.00',
71 ],
72 ],
73 ];
74 $form->setAction(CRM_Core_Action::ADD);
75 $form->_priceSetId = $this->getPriceSetID();
76 $form->submit([
77 'register_date' => date('Ymd'),
78 'status_id' => 5,
79 'role_id' => 1,
80 'event_id' => $form->_eventId,
81 'priceSetId' => $this->getPriceSetID(),
82 $this->getPriceFieldKey() => $this->_ids['price_field_value'][0],
83 'is_pay_later' => 1,
84 'amount_level' => 'Too much',
85 'fee_amount' => 55,
86 'total_amount' => 55,
87 'payment_processor_id' => 0,
88 'record_contribution' => TRUE,
89 'financial_type_id' => 1,
90 'contribution_status_id' => 2,
91 'payment_instrument_id' => 1,
92 'receive_date' => date('Y-m-d'),
93 ]);
94 $participant = $this->callAPISuccessGetSingle('Participant', []);
95 $contribution = $this->callAPISuccessGetSingle('Contribution', []);
96 $this->assertEquals(2, $contribution['contribution_status_id']);
97 $this->callAPISuccessGetSingle('FinancialItem', []);
98
99 $priceSetParams[$this->getPriceFieldKey()] = $this->getPriceFieldValueID();
100 $lineItem = CRM_Price_BAO_LineItem::getLineItems($participant['id'], 'participant');
101 $this->assertEquals(55, $lineItem[1]['subTotal']);
102 $financialItems = $this->callAPISuccess('FinancialItem', 'get', []);
103 $sum = 0;
104 foreach ($financialItems['values'] as $financialItem) {
105 $sum += $financialItem['amount'];
106 }
107 $this->assertEquals(55, $sum);
108
109 CRM_Price_BAO_LineItem::changeFeeSelections($priceSetParams, $participant['id'], 'participant', $contribution['id'], $this->eventFeeBlock, $lineItem);
110 // Check that no payment records have been created.
111 // In https://lab.civicrm.org/dev/financial/issues/94 we had an issue where payments were created when none happend.
112 $payments = $this->callAPISuccess('Payment', 'get', [])['values'];
113 $this->assertCount(0, $payments);
114 $lineItem = CRM_Price_BAO_LineItem::getLineItems($participant['id'], 'participant');
115 // Participants is updated to 0 but line remains.
116 $this->assertEquals(0, $lineItem[1]['subTotal']);
117 $this->assertEquals(1550.55, $lineItem[2]['subTotal']);
118 $financialItems = $this->callAPISuccess('FinancialItem', 'get', []);
119
120 $sum = 0;
121 foreach ($financialItems['values'] as $financialItem) {
122 $sum += $financialItem['amount'];
123 }
124 $this->assertEquals(1550.55, $sum);
125 }
126
127 /**
128 * (dev/core#310) : Test to ensure payments are correctly allocated, when a event fee is changed for a mult-line item event registration
129 *
130 * @throws \CRM_Core_Exception
131 * @throws \CiviCRM_API3_Exception
132 */
133 public function testPaymentAllocationOnMultiLineItemEvent() {
134 // USE-CASE :
135 // 1. Create a Price set with two price fields
136 // 2. Register for a Event using both the price field A($55 - qty 1) and B($10 - qty 1)
137 // 3. Now after registration, edit the participant, change the fee of price B from $10 to $50 (i.e. change qty from 1 to 5)
138 // 4. After submission check that related contribution's status is changed to 'Partially Paid'
139 // 5. Record the additional amount which $40 ($50-$10)
140 // Expected : Check the amount of new Financial Item created is $40
141 $this->createParticipantRecordsFromTwoFieldPriceSet();
142 $priceSetBlock = CRM_Price_BAO_PriceSet::getSetDetail($this->getPriceSetID(), TRUE, FALSE)[$this->getPriceSetID()]['fields'];
143
144 $priceSetParams = [
145 'priceSetId' => $this->getPriceSetID(),
146 // The 1 & 5 refer to qty as they are text fields.
147 'price_' . $this->_ids['price_field']['first_text_field'] => 5,
148 'price_' . $this->_ids['price_field']['second_text_field'] => 1,
149 ];
150 $participant = $this->callAPISuccess('Participant', 'get', []);
151 $lineItem = CRM_Price_BAO_LineItem::getLineItems($participant['id'], 'participant');
152 $contribution = $this->callAPISuccessGetSingle('Contribution', []);
153 CRM_Price_BAO_LineItem::changeFeeSelections($priceSetParams, $participant['id'], 'participant', $contribution['id'], $priceSetBlock, $lineItem);
154
155 $financialItems = $this->callAPISuccess('FinancialItem', 'get', [])['values'];
156 $sum = 0;
157 foreach ($financialItems as $financialItem) {
158 $sum += $financialItem['amount'];
159 }
160 $this->assertEquals(105, $sum);
161 $this->assertCount(3, $financialItems);
162
163 $contribution = $this->callAPISuccessGetSingle('Contribution', []);
164 $this->assertEquals('Partially paid', $contribution['contribution_status']);
165
166 $this->callAPISuccess('Payment', 'create', [
167 'contribution_id' => $contribution['id'],
168 'participant_id' => $participant['id'],
169 'total_amount' => 40.00,
170 'currency' => 'USD',
171 'payment_instrument_id' => 'Check',
172 'check_number' => '#123',
173 ]);
174
175 $result = $this->callAPISuccess('EntityFinancialTrxn', 'get', ['entity_table' => 'civicrm_financial_item', 'sequential' => 1, 'return' => ['entity_table', 'amount']])['values'];
176 $this->assertEquals(40, $result[2]['amount']);
177 $this->assertCount(4, $result);
178 }
179
180 /**
181 * Initial test of submit function.
182 *
183 * @param string $thousandSeparator
184 *
185 * @dataProvider getThousandSeparators
186 *
187 * @throws \Exception
188 */
189 public function testSubmitWithPayment($thousandSeparator) {
190 $this->setCurrencySeparators($thousandSeparator);
191 $form = $this->getForm(['is_monetary' => 1, 'financial_type_id' => 1]);
192 $form->_mode = 'Live';
193 $form->_quickConfig = TRUE;
194 $paymentProcessorID = $this->processorCreate(['is_test' => 0]);
195 $form->submit($this->getSubmitParamsForCreditCardPayment($paymentProcessorID));
196 $participant = $this->callAPISuccessGetSingle('Participant', []);
197 $this->assertEquals('2018-09-04 00:00:00', $participant['participant_register_date']);
198 $this->assertEquals('Offline Registration for Event: Annual CiviCRM meet by: ', $participant['participant_source']);
199 $contribution = $this->callAPISuccessGetSingle('Contribution', []);
200 $this->assertEquals(1550.55, $contribution['total_amount']);
201 $this->assertEquals('Debit Card', $contribution['payment_instrument']);
202 $lineItem = $this->callAPISuccessGetSingle('LineItem', []);
203 $expected = [
204 'contribution_id' => $contribution['id'],
205 'entity_table' => 'civicrm_participant',
206 'qty' => 1,
207 'label' => 'big',
208 'unit_price' => 1550.55,
209 'line_total' => 1550.55,
210 'participant_count' => 0,
211 'price_field_id' => $this->_ids['price_field'][0],
212 'price_field_value_id' => $this->_ids['price_field_value'][1],
213 'tax_amount' => 0,
214 // Interestingly the financial_type_id set in this test is ignored but currently locking in what is happening with this test so setting to 'actual'
215 'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Event Fee'),
216 ];
217 foreach ($expected as $key => $value) {
218 $this->assertEquals($value, $lineItem[$key], $key);
219 }
220 }
221
222 /**
223 * Initial test of submit function.
224 *
225 * @param string $thousandSeparator
226 * @param array $fromEmails From Emails array to overwrite the default.
227 *
228 * @dataProvider getThousandSeparators
229 *
230 * @throws \Exception
231 */
232 public function testSubmitWithFailedPayment($thousandSeparator, $fromEmails = []) {
233 $this->setCurrencySeparators($thousandSeparator);
234 $form = $this->getForm(['is_monetary' => 1, 'financial_type_id' => 1]);
235 $form->_mode = 'Live';
236 $form->_quickConfig = TRUE;
237 $paymentProcessorID = $this->processorCreate(['is_test' => 0]);
238 Civi\Payment\System::singleton()->getById($paymentProcessorID)->setDoDirectPaymentResult(['payment_status_id' => 'failed']);
239
240 $form->_fromEmails = [
241 'from_email_id' => ['abc@gmail.com' => 1],
242 ];
243 try {
244 $form->submit($this->getSubmitParamsForCreditCardPayment($paymentProcessorID));
245 }
246 catch (CRM_Core_Exception_PrematureExitException $e) {
247 return;
248 }
249 $this->fail('should have hit premature exit');
250 }
251
252 /**
253 * Test offline participant mail.
254 *
255 * @param string $thousandSeparator
256 *
257 * @dataProvider getThousandSeparators
258 * @throws \Exception
259 */
260 public function testParticipantOfflineReceipt(string $thousandSeparator): void {
261 $this->setCurrencySeparators($thousandSeparator);
262 $this->swapMessageTemplateForTestTemplate('event_offline_receipt', 'text');
263 $mut = new CiviMailUtils($this, TRUE);
264 // Create an email associated with the logged in contact
265 $loggedInContactID = $this->createLoggedInUser();
266 $email = $this->callAPISuccess('Email', 'create', [
267 'contact_id' => $loggedInContactID,
268 'is_primary' => 1,
269 'email' => 'testLoggedInReceiptEmail@civicrm.org',
270 'location_type_id' => 1,
271 ]);
272
273 //Get workflow id of event_offline receipt.
274 $workflowId = $this->callAPISuccess('OptionValue', 'get', [
275 'return' => ['id'],
276 'option_group_id' => 'msg_tpl_workflow_event',
277 'name' => 'event_offline_receipt',
278 ]);
279
280 //Modify html to contain event_type_id token.
281 $result = $this->callAPISuccess('MessageTemplate', 'get', [
282 'sequential' => 1,
283 'return' => ['id', 'msg_html'],
284 'workflow_id' => $workflowId['id'],
285 'is_default' => 1,
286 ]);
287 $oldMsg = $result['values'][0]['msg_html'];
288 $pos = strpos($oldMsg, 'Please print this confirmation');
289 $newMsg = substr_replace($oldMsg, '<p>Test event type - {$event.event_type_id}</p>', $pos, 0);
290 $this->callAPISuccess('MessageTemplate', 'create', [
291 'id' => $result['id'],
292 'msg_html' => $newMsg,
293 ]);
294
295 // Use the email created as the from email ensuring we are passing a numeric from to test dev/core#1069
296 $this->setCurrencySeparators($thousandSeparator);
297 $form = $this->getForm(['is_monetary' => 1, 'financial_type_id' => 1, 'pay_later_receipt' => 'pay us']);
298 $form->_mode = 'Live';
299 $form->_quickConfig = TRUE;
300 $form->_fromEmails = [
301 'from_email_id' => [$email['id'] => 1],
302 ];
303 $paymentProcessorID = $this->processorCreate(['is_test' => 0]);
304 $submitParams = $this->getSubmitParamsForCreditCardPayment($paymentProcessorID);
305 $submitParams['from_email_address'] = $email['id'];
306 $form->submit($submitParams);
307 $participantID = Participant::get()->addWhere('event_id', '=', $this->getEventID())->execute()->first()['id'];
308 //Check if type is correctly populated in mails.
309 //Also check the string email is present not numeric from.
310 $mut->checkMailLog([
311 'contactID:::' . $this->getContactID(),
312 'contact.id:::' . $this->getContactID(),
313 'eventID:::' . $this->getEventID(),
314 'event.id:::' . $this->getEventID(),
315 'participantID:::' . $participantID,
316 'participant.id:::' . $participantID,
317 '<p>Test event type - 1</p>',
318 'event.title:::Annual CiviCRM meet',
319 'participant.status_id:name:::Registered',
320 'testloggedinreceiptemail@civicrm.org',
321 'event.pay_later_receipt:::pay us',
322 $this->formatMoneyInput(1550.55),
323 ]);
324
325 $this->callAPISuccess('Email', 'delete', ['id' => $email['id']]);
326 }
327
328 /**
329 * Get prepared form object.
330 *
331 * @param array $eventParams
332 *
333 * @return CRM_Event_Form_Participant
334 *
335 * @throws \CRM_Core_Exception
336 */
337 protected function getForm($eventParams = []) {
338 if (!empty($eventParams['is_monetary'])) {
339 $event = $this->eventCreatePaid($eventParams, [['name' => 'big', 'amount' => 1550.55]]);
340 }
341 else {
342 $event = $this->eventCreate($eventParams);
343 }
344
345 $this->ids['contact']['event'] = (int) $this->individualCreate();
346 /** @var CRM_Event_Form_Participant $form */
347 $form = $this->getFormObject('CRM_Event_Form_Participant');
348 $form->_single = TRUE;
349 $form->_contactID = $form->_contactId = $this->ids['contact']['event'];
350 $form->setCustomDataTypes();
351 $form->_eventId = $event['id'];
352 if (!empty($eventParams['is_monetary'])) {
353 $form->_bltID = 5;
354 $form->_isPaidEvent = TRUE;
355 CRM_Event_Form_EventFees::preProcess($form);
356 $form->assignProcessors();
357 $form->buildEventFeeForm($form);
358 }
359 else {
360 $form->_fromEmails = [
361 'from_email_id' => ['abc@gmail.com' => 1],
362 ];
363 }
364 $this->fromEmailAddressOptions = $form->_fromEmails['from_email_id'];
365 return $form;
366 }
367
368 /**
369 * Get a valid value for from_email_address.
370 *
371 * @return int|string
372 */
373 public function getFromEmailAddress() {
374 return key($this->fromEmailAddressOptions);
375 }
376
377 /**
378 * Create a Price set with two price field of type Text.
379 *
380 * Financial Type: 'Event Fee' and 'Event Fee 2' respectively.
381 *
382 * @throws \CRM_Core_Exception
383 * @throws \CiviCRM_API3_Exception
384 */
385 protected function createParticipantRecordsFromTwoFieldPriceSet() {
386 // Create financial type - Event Fee 2
387 $form = $this->getForm(['is_monetary' => 1, 'financial_type_id' => 1]);
388
389 $textFieldsToCreate = [['amount' => 10, 'label' => 'First Text field'], ['amount' => 55, 'label' => 'Second Text field']];
390 foreach ($textFieldsToCreate as $fieldToCreate) {
391 $fieldParams = [
392 'option_label' => ['1' => 'Price Field'],
393 'option_value' => ['1' => $fieldToCreate['amount']],
394 'option_name' => ['1' => $fieldToCreate['amount']],
395 'option_amount' => ['1' => $fieldToCreate['amount']],
396 'option_weight' => ['1' => $fieldToCreate['amount']],
397 'is_display_amounts' => 1,
398 'price_set_id' => $this->_ids['price_set'],
399 'is_enter_qty' => 1,
400 'html_type' => 'Text',
401 'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Campaign Contribution'),
402 ];
403 $fieldParams['label'] = $fieldToCreate['label'];
404 $fieldParams['name'] = CRM_Utils_String::titleToVar($fieldToCreate['label']);
405 $fieldParams['price'] = $fieldToCreate['amount'];
406 $this->_ids['price_field'][strtolower(CRM_Utils_String::titleToVar($fieldToCreate['label']))] = $textPriceFieldID = $this->callAPISuccess('PriceField', 'create', $fieldParams)['id'];
407 $this->_ids['price_field_value'][strtolower(CRM_Utils_String::titleToVar($fieldToCreate['label']))] = (int) $this->callAPISuccess('PriceFieldValue', 'getsingle', ['price_field_id' => $textPriceFieldID])['id'];
408 }
409
410 $form->_lineItem = [
411 0 => [
412 13 => [
413 'price_field_id' => $this->_ids['price_field']['second_text_field'],
414 'price_field_value_id' => $this->_ids['price_field_value']['second_text_field'],
415 'label' => 'Event Fee 1',
416 'field_title' => 'Event Fee 1',
417 'description' => NULL,
418 'qty' => 1,
419 'unit_price' => 55.00,
420 'line_total' => 55.,
421 'participant_count' => 0,
422 'max_value' => NULL,
423 'membership_type_id' => NULL,
424 'membership_num_terms' => NULL,
425 'auto_renew' => NULL,
426 'html_type' => 'Text',
427 'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Campaign Contribution'),
428 'tax_amount' => NULL,
429 'non_deductible_amount' => '0.00',
430 ],
431 14 => [
432 'price_field_id' => $this->_ids['price_field']['first_text_field'],
433 'price_field_value_id' => $this->_ids['price_field_value']['first_text_field'],
434 'label' => 'Event Fee 2',
435 'field_title' => 'Event Fee 2',
436 'description' => NULL,
437 'qty' => 1,
438 'unit_price' => 10.00,
439 'line_total' => 10,
440 'participant_count' => 0,
441 'max_value' => NULL,
442 'membership_type_id' => NULL,
443 'membership_num_terms' => NULL,
444 'auto_renew' => NULL,
445 'html_type' => 'Text',
446 'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Campaign Contribution'),
447 'tax_amount' => NULL,
448 'non_deductible_amount' => '0.00',
449 ],
450 ],
451 ];
452 $form->setAction(CRM_Core_Action::ADD);
453 $form->_priceSetId = $this->_ids['price_set'];
454
455 $form->submit([
456 'register_date' => date('Ymd'),
457 'receive_date' => '2018-09-01',
458 'status_id' => 5,
459 'role_id' => 1,
460 'event_id' => $this->getEventID(),
461 'priceSetId' => $this->_ids['price_set'],
462 'price_' . $this->_ids['price_field']['first_text_field'] => [$this->_ids['price_field_value']['first_text_field'] => 1],
463 'price_' . $this->_ids['price_field']['second_text_field'] => [$this->_ids['price_field_value']['second_text_field'] => 1],
464 'amount_level' => 'Too much',
465 'fee_amount' => 65,
466 'total_amount' => 65,
467 'payment_processor_id' => 0,
468 'record_contribution' => TRUE,
469 'financial_type_id' => 1,
470 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
471 'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
472 ]);
473 }
474
475 /**
476 * Get params for submit function.
477 *
478 * @param int $paymentProcessorID
479 *
480 * @return array
481 */
482 private function getSubmitParamsForCreditCardPayment(int $paymentProcessorID): array {
483 $submitParams = [
484 'register_date' => '2018-09-04',
485 'status_id' => 1,
486 'role_id' => 1,
487 'event_id' => $this->getEventID(),
488 'credit_card_number' => 4444333322221111,
489 'cvv2' => 123,
490 'credit_card_exp_date' => [
491 'M' => 9,
492 'Y' => 2025,
493 ],
494 'credit_card_type' => 'Visa',
495 'billing_first_name' => 'Junko',
496 'billing_middle_name' => '',
497 'billing_last_name' => 'Adams',
498 'billing_street_address-5' => '790L Lincoln St S',
499 'billing_city-5' => 'Maryknoll',
500 'billing_state_province_id-5' => 1031,
501 'billing_postal_code-5' => 10545,
502 'billing_country_id-5' => 1228,
503 'payment_processor_id' => $paymentProcessorID,
504 'priceSetId' => $this->getPriceSetID(),
505 $this->getPriceFieldKey() => $this->getPriceFieldValueID(),
506 'amount_level' => 'Too much',
507 'fee_amount' => $this->formatMoneyInput(1550.55),
508 'total_amount' => $this->formatMoneyInput(1550.55),
509 'from_email_address' => $this->getFromEmailAddress(),
510 'send_receipt' => 1,
511 'receipt_text' => '',
512 ];
513 return $submitParams;
514 }
515
516 /**
517 *
518 * @throws \CRM_Core_Exception
519 * @throws \CiviCRM_API3_Exception
520 */
521 public function testSubmitWithDeferredRecognition() {
522 Civi::settings()->set('deferred_revenue_enabled', TRUE);
523 $futureDate = date('Y') + 1 . '-09-20';
524 $form = $this->getForm(['is_monetary' => 1, 'financial_type_id' => 1, 'start_date' => $futureDate]);
525 $form->_quickConfig = TRUE;
526
527 $form->submit([
528 'register_date' => date('Ymd'),
529 'status_id' => 1,
530 'role_id' => 1,
531 $this->getPriceFieldKey() => $this->getPriceFieldValueID(),
532 'priceSetId' => $this->getPriceSetID(),
533 'event_id' => $this->getEventID(),
534 'record_contribution' => TRUE,
535 'amount_level' => 'blah',
536 'financial_type_id' => 1,
537 ]);
538 $contribution = $this->callAPISuccessGetSingle('Contribution', []);
539 // Api doesn't retrieve it & we don't much want to change that as we want to feature freeze BAO_Query.
540 $this->assertEquals($futureDate . ' 00:00:00', CRM_Core_DAO::singleValueQuery("SELECT revenue_recognition_date FROM civicrm_contribution WHERE id = {$contribution['id']}"));
541 }
542
543 /**
544 * Test submitting a partially paid event registration.
545 *
546 * In this case the participant status is selected as 'partially paid' and
547 * a contribution is created for the full amount with a payment equal to the
548 * entered amount.
549 *
550 * @dataProvider getBooleanDataProvider
551 *
552 * @param bool $isQuickConfig
553 *
554 * @throws \CRM_Core_Exception
555 * @throws \CiviCRM_API3_Exception
556 */
557 public function testSubmitPartialPayment($isQuickConfig) {
558 $mut = new CiviMailUtils($this, TRUE);
559 $form = $this->getForm(['is_monetary' => 1]);
560 $this->callAPISuccess('PriceSet', 'create', ['is_quick_config' => $isQuickConfig, 'id' => $this->getPriceSetID()]);
561 $paymentInstrumentID = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check');
562 $submitParams = [
563 'hidden_feeblock' => '1',
564 'hidden_eventFullMsg' => '',
565 'priceSetId' => $this->getPriceSetID(),
566 $this->getPriceFieldKey() => $this->getPriceFieldValueID(),
567 'check_number' => '879',
568 'record_contribution' => '1',
569 'financial_type_id' => '4',
570 'receive_date' => '2020-01-31 00:51:00',
571 'payment_instrument_id' => $paymentInstrumentID,
572 'trxn_id' => '',
573 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'),
574 'total_amount' => '20',
575 'send_receipt' => '1',
576 'from_email_address' => $this->getFromEmailAddress(),
577 'receipt_text' => 'Contact the Development Department if you need to make any changes to your registration.',
578 'hidden_custom' => '1',
579 'hidden_custom_group_count' => ['' => 1],
580 'custom_4_-1' => '',
581 'contact_id' => $this->getContactID(),
582 'event_id' => $this->getEventID(),
583 'campaign_id' => '',
584 'register_date' => '2020-01-31 00:50:00',
585 'role_id' => [0 => CRM_Core_PseudoConstant::getKey('CRM_Event_BAO_Participant', 'role_id', 'Attendee')],
586 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Event_BAO_Participant', 'status_id', 'Partially paid'),
587 'source' => 'I wrote this',
588 'note' => 'I wrote a note',
589 'MAX_FILE_SIZE' => '33554432',
590 ];
591 $form->submit($submitParams);
592 $this->assertPartialPaymentResult($isQuickConfig, $mut);
593 }
594
595 /**
596 * Test submitting a partially paid event registration, recording a pending contribution.
597 *
598 * This tests
599 *
600 * @dataProvider getBooleanDataProvider
601 *
602 * @param bool $isQuickConfig
603 *
604 * @throws \CRM_Core_Exception
605 * @throws \CiviCRM_API3_Exception
606 */
607 public function testSubmitPendingPartiallyPaidAddPayment($isQuickConfig) {
608 $mut = new CiviMailUtils($this, TRUE);
609 $form = $this->getForm(['is_monetary' => 1]);
610 $this->callAPISuccess('PriceSet', 'create', ['is_quick_config' => $isQuickConfig, 'id' => $this->getPriceSetID()]);
611 $paymentInstrumentID = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check');
612 $submitParams = $this->getRecordContributionParams('Partially paid', $form);
613 $form->submit($submitParams);
614 $this->callAPISuccess('Payment', 'create', [
615 'contribution_id' => $this->callAPISuccessGetValue('Contribution', ['return' => 'id']),
616 'total_amount' => 20,
617 'check_number' => 879,
618 'payment_instrument_id' => $paymentInstrumentID,
619 ]);
620 $this->assertPartialPaymentResult($isQuickConfig, $mut);
621 }
622
623 /**
624 * Test submitting a partially paid event registration, recording a pending contribution.
625 *
626 * This tests
627 *
628 * @dataProvider getBooleanDataProvider
629 *
630 * @param bool $isQuickConfig
631 *
632 * @throws \CRM_Core_Exception
633 */
634 public function testSubmitPendingAddPayment($isQuickConfig) {
635 $mut = new CiviMailUtils($this, TRUE);
636 $form = $this->getForm(['is_monetary' => 1]);
637 $this->callAPISuccess('PriceSet', 'create', ['is_quick_config' => $isQuickConfig, 'id' => $this->getPriceSetID()]);
638 $paymentInstrumentID = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check');
639 $submitParams = $this->getRecordContributionParams('Pending from pay later', 'Pending');
640 // Create the pending contribution for the full amount to be paid.
641 $submitParams['total_amount'] = 1550.55;
642 $form->submit($submitParams);
643 $this->callAPISuccess('Payment', 'create', [
644 'contribution_id' => $this->callAPISuccessGetValue('Contribution', ['return' => 'id']),
645 'total_amount' => 20,
646 'check_number' => 879,
647 'payment_instrument_id' => $paymentInstrumentID,
648 ]);
649 $this->assertPartialPaymentResult($isQuickConfig, $mut, FALSE);
650 }
651
652 /**
653 * @param bool $isQuickConfig
654 * @param \CiviMailUtils $mut
655 * @param bool $isAmountPaidOnForm
656 * Was the amount paid entered on the form (if so this should be on the receipt)
657 */
658 protected function assertPartialPaymentResult($isQuickConfig, CiviMailUtils $mut, $isAmountPaidOnForm = TRUE) {
659 $paymentInstrumentID = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check');
660 $contribution = $this->callAPISuccessGetSingle('Contribution', []);
661 $expected = [
662 'contact_id' => $this->getContactID(),
663 'total_amount' => '1550.55',
664 'fee_amount' => '0.00',
665 'net_amount' => '1550.55',
666 'contribution_source' => 'I wrote this',
667 'amount_level' => '',
668 'is_template' => '0',
669 'financial_type' => 'Event Fee',
670 'payment_instrument' => 'Check',
671 'contribution_status' => 'Partially paid',
672 'check_number' => '879',
673 ];
674 $this->assertAttributesEquals($expected, $contribution);
675
676 $participant = $this->callAPISuccessGetSingle('Participant', []);
677 $this->assertAttributesEquals([
678 'contact_id' => $this->getContactID(),
679 'event_title' => 'Annual CiviCRM meet',
680 'participant_fee_level' => [0 => 'big - 1'],
681 'participant_fee_amount' => '1550.55',
682 'participant_fee_currency' => 'USD',
683 'event_type' => 'Conference',
684 'participant_status' => 'Partially paid',
685 'participant_role' => 'Attendee',
686 'participant_source' => 'I wrote this',
687 'participant_note' => 'I wrote a note',
688 'participant_is_pay_later' => '0',
689 ], $participant);
690 $lineItem = $this->callAPISuccessGetSingle('LineItem', []);
691 $this->assertAttributesEquals([
692 'entity_table' => 'civicrm_participant',
693 'entity_id' => $participant['id'],
694 'contribution_id' => $contribution['id'],
695 'price_field_id' => $this->getPriceFieldID(),
696 'label' => 'big',
697 'qty' => '1.00',
698 'unit_price' => '1550.55',
699 'line_total' => '1550.55',
700 'participant_count' => '0',
701 'price_field_value_id' => $this->getPriceFieldValueID(),
702 'financial_type_id' => '4',
703 'tax_amount' => '0.00',
704 ], $lineItem);
705
706 $payment = $this->callAPISuccessGetSingle('FinancialTrxn', ['is_payment' => 1]);
707 $this->assertAttributesEquals([
708 'to_financial_account_id' => 6,
709 'from_financial_account_id' => 7,
710 'total_amount' => 20,
711 'fee_amount' => '0.00',
712 'net_amount' => 20,
713 'currency' => 'USD',
714 'status_id' => '1',
715 'payment_instrument_id' => $paymentInstrumentID,
716 'check_number' => '879',
717 ], $payment);
718
719 $financialItem = $this->callAPISuccessGetSingle('FinancialItem', []);
720 $this->assertAttributesEquals([
721 'description' => 'big',
722 'contact_id' => $this->getContactID(),
723 'amount' => 1550.55,
724 'currency' => 'USD',
725 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Financial_BAO_FinancialItem', 'status_id', 'Partially paid'),
726 'entity_table' => 'civicrm_line_item',
727 'entity_id' => $lineItem['id'],
728 'financial_account_id' => 4,
729 ], $financialItem);
730
731 $mut->checkMailLog([
732 'From: "FIXME" <info@EXAMPLE.ORG>',
733 'To: Anthony Anderson <anthony_anderson@civicrm.org>',
734 'Subject: Event Confirmation - Annual CiviCRM meet - Mr. Anthony Anderson II',
735 'Dear Anthony,Contact the Development Department if you need to make any changes to your registration.',
736 'Event Information and Location',
737 'Annual CiviCRM meet',
738 'Registered Email',
739 $isQuickConfig ? $this->formatMoneyInput(1550.55) . ' big - 1' : 'Price Field - big',
740 $isAmountPaidOnForm ? 'Total Paid: $20.00' : ' ',
741 'Balance: $1,530.55',
742 'Financial Type: Event Fee',
743 'Paid By: Check',
744 'Check Number: 879',
745 ]);
746 }
747
748 /**
749 * Get the id of the configured price set.
750 *
751 * @return int
752 */
753 protected function getPriceSetID() {
754 return (int) $this->_ids['price_set'];
755 }
756
757 /**
758 * Get the price field id that has been created for the test.
759 *
760 * @return int
761 */
762 protected function getPriceFieldID() {
763 return (int) $this->_ids['price_field'][0];
764 }
765
766 /**
767 * Get the array key for the configured price field.
768 *
769 * @return string
770 */
771 protected function getPriceFieldKey(): string {
772 return 'price_' . $this->getPriceFieldID();
773 }
774
775 /**
776 * Get the price field value id that has been created for the test.
777 *
778 * @return int
779 */
780 protected function getPriceFieldValueID(): int {
781 return (int) $this->_ids['price_field_value'][1];
782 }
783
784 /**
785 * Get the parameters for recording a contribution.
786 *
787 * @param string $participantStatus
788 * @param string $contributionStatus
789 *
790 * @return array
791 */
792 protected function getRecordContributionParams($participantStatus, $contributionStatus): array {
793 $submitParams = [
794 'hidden_feeblock' => '1',
795 'hidden_eventFullMsg' => '',
796 'priceSetId' => $this->getPriceSetID(),
797 $this->getPriceFieldKey() => $this->getPriceFieldValueID(),
798 'check_number' => '879',
799 'record_contribution' => '1',
800 'financial_type_id' => '4',
801 'receive_date' => '2020-01-31 00:51:00',
802 'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
803 'trxn_id' => '',
804 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $contributionStatus),
805 'total_amount' => '20',
806 'send_receipt' => '1',
807 'from_email_address' => $this->getFromEmailAddress(),
808 'receipt_text' => 'Contact the Development Department if you need to make any changes to your registration.',
809 'hidden_custom' => '1',
810 'hidden_custom_group_count' => ['' => 1],
811 'custom_4_-1' => '',
812 'contact_id' => $this->getContactID(),
813 'event_id' => $this->getEventID(),
814 'campaign_id' => '',
815 'register_date' => '2020-01-31 00:50:00',
816 'role_id' => [0 => CRM_Core_PseudoConstant::getKey('CRM_Event_BAO_Participant', 'role_id', 'Attendee')],
817 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Event_BAO_Participant', 'status_id', $participantStatus),
818 'source' => 'I wrote this',
819 'note' => 'I wrote a note',
820 'MAX_FILE_SIZE' => '33554432',
821 ];
822 return $submitParams;
823 }
824
825 /**
826 * Check if participant is transferred correctly.
827 *
828 * @throws \CRM_Core_Exception
829 * @throws \CiviCRM_API3_Exception
830 */
831 public function testTransferParticipantRegistration(): void {
832 //Register a contact to a sample event.
833 $this->createEventOrder();
834 $contribution = $this->callAPISuccessGetSingle('Contribution', ['return' => 'id']);
835 //Check line item count of the contribution id before transfer.
836 $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($contribution['id']);
837 $this->assertCount(2, $lineItems);
838 $participantId = CRM_Core_DAO::getFieldValue('CRM_Event_BAO_ParticipantPayment', $contribution['id'], 'participant_id', 'contribution_id');
839 /* @var CRM_Event_Form_SelfSvcTransfer $form */
840 $form = $this->getFormObject('CRM_Event_Form_SelfSvcTransfer');
841 $toContactId = $this->individualCreate();
842 $mut = new CiviMailUtils($this);
843 $this->swapMessageTemplateForInput('event_online_receipt', '{domain.name} {contact.first_name}');
844 $form->transferParticipantRegistration($toContactId, $participantId);
845 $mut->checkAllMailLog(['Default Domain Name Anthony']);
846 $mut->clearMessages();
847 $this->revertTemplateToReservedTemplate('event_online_receipt', 'html');
848
849 //Assert participant is transferred to $toContactId.
850 $participant = $this->callAPISuccess('Participant', 'getsingle', [
851 'return' => ['transferred_to_contact_id'],
852 'id' => $participantId,
853 ]);
854 $this->assertEquals($participant['transferred_to_contact_id'], $toContactId);
855
856 //Assert $toContactId has a new registration.
857 $toParticipant = $this->callAPISuccess('Participant', 'getsingle', [
858 'contact_id' => $toContactId,
859 ]);
860 $this->assertEquals($toParticipant['participant_registered_by_id'], $participantId);
861
862 //Check line item count of the contribution id remains the same.
863 $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($contribution['id']);
864 $this->assertCount(2, $lineItems);
865 // There should be 2 participant payments on the contribution & 0 others existing.
866 $this->callAPISuccessGetCount('ParticipantPayment', ['contribution_id' => $contribution['id']], 2);
867 $this->callAPISuccessGetCount('ParticipantPayment', [], 2);
868 $this->callAPISuccessGetCount('ParticipantPayment', ['participant_id' => $toParticipant['id']], 1);
869 $this->callAPISuccessGetCount('ParticipantPayment', ['participant_id' => $participantId], 0);
870 }
871
872 /**
873 * Get the id of the created event.
874 *
875 * @return int
876 */
877 protected function getEventID(): int {
878 return $this->ids['Event']['event'];
879 }
880
881 /**
882 * Get created contact ID.
883 *
884 * @return int
885 */
886 protected function getContactID(): int {
887 return $this->ids['contact']['event'];
888 }
889
890 }