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