Rewrite submitOnce function, deprecate old one
[civicrm-core.git] / CRM / Event / Form / Registration / Confirm.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 * @package CRM
30 * @copyright CiviCRM LLC (c) 2004-2019
31 */
32
33 /**
34 * This class generates form components for processing Event.
35 */
36 class CRM_Event_Form_Registration_Confirm extends CRM_Event_Form_Registration {
37
38 /**
39 * The values for the contribution db object.
40 *
41 * @var array
42 */
43 public $_values;
44
45 /**
46 * The total amount.
47 *
48 * @var float
49 */
50 public $_totalAmount;
51
52 public $submitOnce = TRUE;
53
54 /**
55 * Monetary fields that may be submitted.
56 *
57 * These should get a standardised format in the beginPostProcess function.
58 *
59 * These fields are common to many forms. Some may override this.
60 * @var array
61 */
62 protected $submittableMoneyFields = ['total_amount', 'net_amount', 'non_deductible_amount', 'fee_amount', 'tax_amount', 'amount'];
63
64 /**
65 * Set variables up before form is built.
66 */
67 public function preProcess() {
68 parent::preProcess();
69
70 // lineItem isn't set until Register postProcess
71 $this->_lineItem = $this->get('lineItem');
72
73 $this->_params = $this->get('params');
74 $this->_params[0]['tax_amount'] = $this->get('tax_amount');
75
76 $this->_params[0]['is_pay_later'] = $this->get('is_pay_later');
77 $this->assign('is_pay_later', $this->_params[0]['is_pay_later']);
78 if ($this->_params[0]['is_pay_later']) {
79 $this->assign('pay_later_receipt', $this->_values['event']['pay_later_receipt']);
80 }
81
82 CRM_Utils_Hook::eventDiscount($this, $this->_params);
83
84 if (!empty($this->_params[0]['discount']) && !empty($this->_params[0]['discount']['applied'])) {
85 $this->set('hookDiscount', $this->_params[0]['discount']);
86 $this->assign('hookDiscount', $this->_params[0]['discount']);
87 }
88
89 if (!$this->preProcessExpress()) {
90 //process only primary participant params.
91 $registerParams = $this->_params[0];
92 if (isset($registerParams["billing_state_province_id-{$this->_bltID}"])
93 && $registerParams["billing_state_province_id-{$this->_bltID}"]
94 ) {
95 $registerParams["billing_state_province-{$this->_bltID}"] = CRM_Core_PseudoConstant::stateProvinceAbbreviation($registerParams["billing_state_province_id-{$this->_bltID}"]);
96 }
97
98 if (isset($registerParams["billing_country_id-{$this->_bltID}"]) && $registerParams["billing_country_id-{$this->_bltID}"]) {
99 $registerParams["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant::countryIsoCode($registerParams["billing_country_id-{$this->_bltID}"]);
100 }
101 if (isset($registerParams['credit_card_exp_date'])) {
102 $registerParams['year'] = CRM_Core_Payment_Form::getCreditCardExpirationYear($registerParams);
103 $registerParams['month'] = CRM_Core_Payment_Form::getCreditCardExpirationMonth($registerParams);
104 }
105 if ($this->_values['event']['is_monetary']) {
106 $registerParams['ip_address'] = CRM_Utils_System::ipAddress();
107 $registerParams['currencyID'] = $this->_params[0]['currencyID'];
108 }
109 //assign back primary participant params.
110 $this->_params[0] = $registerParams;
111 }
112
113 if ($this->_values['event']['is_monetary']) {
114 $this->_params[0]['invoiceID'] = $this->get('invoiceID');
115 }
116 $this->assign('defaultRole', FALSE);
117 if (CRM_Utils_Array::value('defaultRole', $this->_params[0]) == 1) {
118 $this->assign('defaultRole', TRUE);
119 }
120
121 if (empty($this->_params[0]['participant_role_id']) &&
122 $this->_values['event']['default_role_id']
123 ) {
124 $this->_params[0]['participant_role_id'] = $this->_values['event']['default_role_id'];
125 }
126
127 if (isset($this->_values['event']['confirm_title'])) {
128 CRM_Utils_System::setTitle($this->_values['event']['confirm_title']);
129 }
130
131 // Personal campaign page
132 if ($this->_pcpId) {
133 $params = CRM_Contribute_Form_Contribution_Confirm::processPcp($this, $this->_params[0]);
134 $this->_params[0] = $params;
135 }
136
137 $this->set('params', $this->_params);
138 }
139
140 /**
141 * Pre process function for Paypal Express confirm.
142 * @todo this is just a step in refactor as payment processor specific code does not belong in generic forms
143 *
144 * @return bool
145 * @throws \CRM_Core_Exception
146 */
147 private function preProcessExpress() {
148 if ($this->_contributeMode !== 'express') {
149 return FALSE;
150 }
151 $params = [];
152 // rfp == redirect from paypal
153 // @fixme rfp is probably not required - the getPreApprovalDetails should deal with any payment-processor specific 'stuff'
154 $rfp = CRM_Utils_Request::retrieve('rfp', 'Boolean', CRM_Core_DAO::$_nullObject, FALSE, NULL, 'GET');
155
156 //we lost rfp in case of additional participant. So set it explicitly.
157 if ($rfp || CRM_Utils_Array::value('additional_participants', $this->_params[0], FALSE)) {
158 if (!empty($this->_paymentProcessor) && $this->_paymentProcessor['object']->supports('preApproval')) {
159 $preApprovalParams = $this->_paymentProcessor['object']->getPreApprovalDetails($this->get('pre_approval_parameters'));
160 $params = array_merge($this->_params, $preApprovalParams);
161 }
162 CRM_Core_Payment_Form::mapParams($this->_bltID, $params, $params, FALSE);
163
164 // set a few other parameters that are not really specific to this method because we don't know what
165 // will break if we change this.
166 $params['amount'] = $this->_params[0]['amount'];
167 if (!empty($this->_params[0]['discount'])) {
168 $params['discount'] = $this->_params[0]['discount'];
169 $params['discountAmount'] = $this->_params[0]['discountAmount'];
170 $params['discountMessage'] = $this->_params[0]['discountMessage'];
171 }
172
173 $params['amount_level'] = $this->_params[0]['amount_level'];
174 $params['currencyID'] = $this->_params[0]['currencyID'];
175
176 // also merge all the other values from the profile fields
177 $values = $this->controller->exportValues('Register');
178 $skipFields = [
179 'amount',
180 "street_address-{$this->_bltID}",
181 "city-{$this->_bltID}",
182 "state_province_id-{$this->_bltID}",
183 "postal_code-{$this->_bltID}",
184 "country_id-{$this->_bltID}",
185 ];
186
187 foreach ($values as $name => $value) {
188 // skip amount field
189 if (!in_array($name, $skipFields)) {
190 $params[$name] = $value;
191 }
192 if (substr($name, 0, 6) == 'price_') {
193 $params[$name] = $this->_params[0][$name];
194 }
195 }
196 $this->set('getExpressCheckoutDetails', $params);
197 }
198 $this->_params[0] = array_merge($this->_params[0], $params);
199 $this->_params[0]['is_primary'] = 1;
200 return TRUE;
201 }
202
203 /**
204 * Overwrite action, since we are only showing elements in frozen mode no help display needed.
205 *
206 * @return int
207 */
208 public function getAction() {
209 if ($this->_action & CRM_Core_Action::PREVIEW) {
210 return CRM_Core_Action::VIEW | CRM_Core_Action::PREVIEW;
211 }
212 else {
213 return CRM_Core_Action::VIEW;
214 }
215 }
216
217 /**
218 * Build the form object.
219 */
220 public function buildQuickForm() {
221 $this->assignToTemplate();
222
223 if ($this->_values['event']['is_monetary'] &&
224 ($this->_params[0]['amount'] || $this->_params[0]['amount'] == 0) &&
225 !$this->_requireApproval
226 ) {
227 $this->_amount = [];
228
229 $taxAmount = 0;
230 foreach ($this->_params as $k => $v) {
231 if ($v == 'skip') {
232 continue;
233 }
234 $individualTaxAmount = 0;
235 //display tax amount on confirmation page
236 $taxAmount += $v['tax_amount'];
237 if (is_array($v)) {
238 $this->cleanMoneyFields($v);
239 foreach ([
240 'first_name',
241 'last_name',
242 ] as $name) {
243 if (isset($v['billing_' . $name]) &&
244 !isset($v[$name])
245 ) {
246 $v[$name] = $v['billing_' . $name];
247 }
248 }
249
250 if (!empty($v['first_name']) && !empty($v['last_name'])) {
251 $append = $v['first_name'] . ' ' . $v['last_name'];
252 }
253 else {
254 //use an email if we have one
255 foreach ($v as $v_key => $v_val) {
256 if (substr($v_key, 0, 6) == 'email-') {
257 $append = $v[$v_key];
258 }
259 }
260 }
261
262 $this->_amount[$k]['amount'] = $v['amount'];
263 if (!empty($v['discountAmount'])) {
264 $this->_amount[$k]['amount'] -= $v['discountAmount'];
265 }
266
267 $this->_amount[$k]['label'] = preg_replace('/\ 1/', '', $v['amount_level']) . ' - ' . $append;
268 $this->_part[$k]['info'] = CRM_Utils_Array::value('first_name', $v) . ' ' . CRM_Utils_Array::value('last_name', $v);
269 if (empty($v['first_name'])) {
270 $this->_part[$k]['info'] = $append;
271 }
272
273 /*CRM-16320 */
274 $individual[$k]['totalAmtWithTax'] = $this->_amount[$k]['amount'];
275 $individual[$k]['totalTaxAmt'] = $individualTaxAmount + $v['tax_amount'];
276 $this->_totalAmount = $this->_totalAmount + $this->_amount[$k]['amount'];
277 if (!empty($v['is_primary'])) {
278 $this->set('primaryParticipantAmount', $this->_amount[$k]['amount']);
279 }
280 }
281 }
282
283 $invoiceSettings = Civi::settings()->get('contribution_invoice_settings');
284 $taxTerm = CRM_Utils_Array::value('tax_term', $invoiceSettings);
285 $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings);
286 if ($invoicing) {
287 $this->assign('totalTaxAmount', $taxAmount);
288 $this->assign('taxTerm', $taxTerm);
289 $this->assign('individual', $individual);
290 $this->set('individual', $individual);
291 }
292
293 $this->assign('part', $this->_part);
294 $this->set('part', $this->_part);
295 $this->assign('amounts', $this->_amount);
296 $this->assign('totalAmount', $this->_totalAmount);
297 $this->set('totalAmount', $this->_totalAmount);
298 }
299
300 if ($this->_priceSetId && !CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'is_quick_config')) {
301 $lineItemForTemplate = [];
302 $getTaxDetails = FALSE;
303 if (!empty($this->_lineItem) && is_array($this->_lineItem)) {
304 foreach ($this->_lineItem as $key => $value) {
305 if (!empty($value)) {
306 $lineItemForTemplate[$key] = $value;
307 }
308 if ($invoicing) {
309 foreach ($value as $v) {
310 if (isset($v['tax_rate'])) {
311 $getTaxDetails = TRUE;
312 }
313 }
314 }
315 }
316 }
317 if (!empty($lineItemForTemplate)) {
318 $this->assign('lineItem', $lineItemForTemplate);
319 }
320 $this->assign('getTaxDetails', $getTaxDetails);
321 }
322
323 //display additional participants profile.
324 self::assignProfiles($this);
325
326 //consider total amount.
327 $this->assign('isAmountzero', ($this->_totalAmount <= 0) ? TRUE : FALSE);
328
329 $contribButton = ts('Continue');
330 $this->addButtons([
331 [
332 'type' => 'back',
333 'name' => ts('Go Back'),
334 ],
335 [
336 'type' => 'next',
337 'name' => $contribButton,
338 'isDefault' => TRUE,
339 ],
340 ]);
341
342 $defaults = [];
343 $fields = [];
344 if (!empty($this->_fields)) {
345 foreach ($this->_fields as $name => $dontCare) {
346 $fields[$name] = 1;
347 }
348 }
349 $fields["billing_state_province-{$this->_bltID}"] = $fields["billing_country-{$this->_bltID}"] = $fields["email-{$this->_bltID}"] = 1;
350 foreach ($fields as $name => $dontCare) {
351 if (isset($this->_params[0][$name])) {
352 $defaults[$name] = $this->_params[0][$name];
353 if (substr($name, 0, 7) == 'custom_') {
354 $timeField = "{$name}_time";
355 if (isset($this->_params[0][$timeField])) {
356 $defaults[$timeField] = $this->_params[0][$timeField];
357 }
358 if (isset($this->_params[0]["{$name}_id"])) {
359 $defaults["{$name}_id"] = $this->_params[0]["{$name}_id"];
360 }
361 }
362 elseif (in_array($name, CRM_Contact_BAO_Contact::$_greetingTypes)
363 && !empty($this->_params[0][$name . '_custom'])
364 ) {
365 $defaults[$name . '_custom'] = $this->_params[0][$name . '_custom'];
366 }
367 }
368 }
369
370 $this->setDefaults($defaults);
371 $this->freeze();
372
373 //lets give meaningful status message, CRM-4320.
374 $this->assign('isOnWaitlist', $this->_allowWaitlist);
375 $this->assign('isRequireApproval', $this->_requireApproval);
376
377 // Assign Participant Count to Lineitem Table
378 $this->assign('pricesetFieldsCount', CRM_Price_BAO_PriceSet::getPricesetCount($this->_priceSetId));
379 $this->addFormRule(['CRM_Event_Form_Registration_Confirm', 'formRule'], $this);
380 }
381
382 /**
383 * Apply form rule.
384 *
385 * @param array $fields
386 * @param array $files
387 * @param CRM_Core_Form $self
388 *
389 * @return array|bool
390 */
391 public static function formRule($fields, $files, $self) {
392 $errors = [];
393 $eventFull = CRM_Event_BAO_Participant::eventFull($self->_eventId, FALSE, CRM_Utils_Array::value('has_waitlist', $self->_values['event']));
394 if ($eventFull && empty($self->_allowConfirmation)) {
395 if (empty($self->_allowWaitlist)) {
396 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/event/register', "reset=1&id={$self->_eventId}", FALSE, NULL, FALSE, TRUE));
397 }
398 }
399 $self->_feeBlock = $self->_values['fee'];
400 CRM_Event_Form_Registration_Register::formatFieldsForOptionFull($self);
401
402 if (!empty($self->_priceSetId) &&
403 !$self->_requireApproval && !$self->_allowWaitlist
404 ) {
405 $priceSetErrors = self::validatePriceSet($self, $self->_params);
406 if (!empty($priceSetErrors)) {
407 CRM_Core_Session::setStatus(ts('You have been returned to the start of the registration process and any sold out events have been removed from your selections. You will not be able to continue until you review your booking and select different events if you wish.'), ts('Unfortunately some of your options have now sold out for one or more participants.'), 'error');
408 CRM_Core_Session::setStatus(ts('Please note that the options which are marked or selected are sold out for participant being viewed.'), ts('Sold out:'), 'error');
409 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/event/register', "_qf_Register_display=true&qfKey={$fields['qfKey']}"));
410 }
411 }
412
413 return empty($priceSetErrors) ? TRUE : $priceSetErrors;
414 }
415
416 /**
417 * Process the form submission.
418 */
419 public function postProcess() {
420 $now = date('YmdHis');
421
422 $this->_params = $this->get('params');
423 $this->cleanMoneyFields($this->_params);
424
425 if (!empty($this->_params[0]['contact_id'])) {
426 // unclear when this would be set & whether it could be checked in getContactID.
427 // perhaps it relates to when cid is in the url
428 //@todo someone who knows add comments on the various contactIDs in this form
429 $contactID = $this->_params[0]['contact_id'];
430 }
431 else {
432 $contactID = $this->getContactID();
433 }
434
435 // if a discount has been applied, lets now deduct it from the amount
436 // and fix the fee level
437 if (!empty($this->_params[0]['discount']) && !empty($this->_params[0]['discount']['applied'])) {
438 foreach ($this->_params as $k => $v) {
439 if (CRM_Utils_Array::value('amount', $this->_params[$k]) > 0 && !empty($this->_params[$k]['discountAmount'])) {
440 $this->_params[$k]['amount'] -= $this->_params[$k]['discountAmount'];
441 $this->_params[$k]['amount_level'] .= CRM_Utils_Array::value('discountMessage', $this->_params[$k]);
442 }
443 }
444 $this->set('params', $this->_params);
445 }
446
447 // CRM-4320, lets build array of cancelled additional participant ids
448 // those are drop or skip by primary at the time of confirmation.
449 // get all in and then unset those we want to process.
450 $cancelledIds = $this->_additionalParticipantIds;
451
452 $params = $this->_params;
453 if ($this->_values['event']['is_monetary']) {
454 $this->set('finalAmount', $this->_amount);
455 }
456 $participantCount = [];
457 $taxAmount = $totalTaxAmount = 0;
458
459 //unset the skip participant from params.
460 //build the $participantCount array.
461 //maintain record for all participants.
462 foreach ($params as $participantNum => $record) {
463 if ($record == 'skip') {
464 unset($params[$participantNum]);
465 $participantCount[$participantNum] = 'skip';
466 }
467 elseif ($participantNum) {
468 $participantCount[$participantNum] = 'participant';
469 }
470 $totalTaxAmount += CRM_Utils_Array::value('tax_amount', $record, 0);
471 if (CRM_Utils_Array::value('is_primary', $record)) {
472 $taxAmount = &$params[$participantNum]['tax_amount'];
473 }
474 //lets get additional participant id to cancel.
475 if ($this->_allowConfirmation && is_array($cancelledIds)) {
476 $additonalId = CRM_Utils_Array::value('participant_id', $record);
477 if ($additonalId && $key = array_search($additonalId, $cancelledIds)) {
478 unset($cancelledIds[$key]);
479 }
480 }
481 }
482 $taxAmount = $totalTaxAmount;
483 $payment = $registerByID = $primaryCurrencyID = $contribution = NULL;
484 $paymentObjError = ts('The system did not record payment details for this payment and so could not process the transaction. Please report this error to the site administrator.');
485
486 $this->participantIDS = [];
487 $fields = [];
488 foreach ($params as $key => $value) {
489 CRM_Event_Form_Registration_Confirm::fixLocationFields($value, $fields, $this);
490 //unset the billing parameters if it is pay later mode
491 //to avoid creation of billing location
492 // @todo - the reasoning for this is unclear - elsewhere we check what fields are provided by
493 // the form & if billing fields exist we create the address, relying on the form to collect
494 // only information we intend to store.
495 if ($this->_allowWaitlist
496 || $this->_requireApproval
497 || (!empty($value['is_pay_later']) && !$this->_isBillingAddressRequiredForPayLater)
498 || empty($value['is_primary'])
499 ) {
500 $billingFields = [
501 "email-{$this->_bltID}",
502 'billing_first_name',
503 'billing_middle_name',
504 'billing_last_name',
505 "billing_street_address-{$this->_bltID}",
506 "billing_city-{$this->_bltID}",
507 "billing_state_province-{$this->_bltID}",
508 "billing_state_province_id-{$this->_bltID}",
509 "billing_postal_code-{$this->_bltID}",
510 "billing_country-{$this->_bltID}",
511 "billing_country_id-{$this->_bltID}",
512 "address_name-{$this->_bltID}",
513 ];
514 foreach ($billingFields as $field) {
515 unset($value[$field]);
516 }
517 if (!empty($value['is_pay_later'])) {
518 $this->_values['params']['is_pay_later'] = TRUE;
519 }
520 }
521
522 //Unset ContactID for additional participants and set RegisterBy Id.
523 if (empty($value['is_primary'])) {
524 $contactID = CRM_Utils_Array::value('contact_id', $value);
525 $registerByID = $this->get('registerByID');
526 if ($registerByID) {
527 $value['registered_by_id'] = $registerByID;
528 }
529 }
530 else {
531 $value['amount'] = $this->_totalAmount;
532 }
533
534 $contactID = CRM_Event_Form_Registration_Confirm::updateContactFields($contactID, $value, $fields, $this);
535
536 // lets store the contactID in the session
537 // we dont store in userID in case the user is doing multiple
538 // transactions etc
539 // for things like tell a friend
540 if (!$this->getContactID() && !empty($value['is_primary'])) {
541 CRM_Core_Session::singleton()->set('transaction.userID', $contactID);
542 }
543
544 $value['description'] = ts('Online Event Registration') . ': ' . $this->_values['event']['title'];
545 $value['accountingCode'] = CRM_Utils_Array::value('accountingCode',
546 $this->_values['event']
547 );
548
549 $pending = FALSE;
550 if ($this->_allowWaitlist || $this->_requireApproval) {
551 //get the participant statuses.
552 $waitingStatuses = CRM_Event_PseudoConstant::participantStatus(NULL, "class = 'Waiting'");
553 if ($this->_allowWaitlist) {
554 $value['participant_status_id'] = $value['participant_status'] = array_search('On waitlist', $waitingStatuses);
555 }
556 else {
557 $value['participant_status_id'] = $value['participant_status'] = array_search('Awaiting approval', $waitingStatuses);
558 }
559
560 //there might be case user selected pay later and
561 //now becomes part of run time waiting list.
562 $value['is_pay_later'] = FALSE;
563 }
564 elseif ($this->_values['event']['is_monetary']) {
565 // required only if paid event
566 if (is_array($this->_paymentProcessor)) {
567 $payment = $this->_paymentProcessor['object'];
568 }
569 if (!empty($this->_paymentProcessor) && $this->_paymentProcessor['object']->supports('preApproval')) {
570 $preApprovalParams = $this->_paymentProcessor['object']->getPreApprovalDetails($this->get('pre_approval_parameters'));
571 $value = array_merge($value, $preApprovalParams);
572 }
573 $result = NULL;
574
575 if (!empty($value['is_pay_later']) ||
576 $value['amount'] == 0 ||
577 // The concept of contributeMode is deprecated.
578 $this->_contributeMode == 'checkout' ||
579 $this->_contributeMode == 'notify'
580 ) {
581 if ($value['amount'] != 0) {
582 $pending = TRUE;
583 //get the participant statuses.
584 $pendingStatuses = CRM_Event_PseudoConstant::participantStatus(NULL, "class = 'Pending'");
585 $status = !empty($value['is_pay_later']) ? 'Pending from pay later' : 'Pending from incomplete transaction';
586 $value['participant_status_id'] = $value['participant_status'] = array_search($status, $pendingStatuses);
587 }
588 }
589 elseif (!empty($value['is_primary'])) {
590 CRM_Core_Payment_Form::mapParams($this->_bltID, $value, $value, TRUE);
591 // payment email param can be empty for _bltID mapping
592 // thus provide mapping for it with a different email value
593 if (empty($value['email'])) {
594 $value['email'] = CRM_Utils_Array::valueByRegexKey('/^email-/', $value);
595 }
596
597 if (is_object($payment)) {
598 // Not quite sure why we don't just user $value since it contains the data
599 // from result
600 // @todo ditch $result & retest.
601 list($result, $value) = $this->processPayment($payment, $value);
602 }
603 else {
604 CRM_Core_Error::fatal($paymentObjError);
605 }
606 }
607
608 $value['receive_date'] = $now;
609 if ($this->_allowConfirmation) {
610 $value['participant_register_date'] = $this->_values['participant']['register_date'];
611 }
612
613 $createContrib = ($value['amount'] != 0) ? TRUE : FALSE;
614 // force to create zero amount contribution, CRM-5095
615 if (!$createContrib && ($value['amount'] == 0)
616 && $this->_priceSetId && $this->_lineItem
617 ) {
618 $createContrib = TRUE;
619 }
620
621 if ($createContrib && !empty($value['is_primary']) &&
622 !$this->_allowWaitlist && !$this->_requireApproval
623 ) {
624 // if paid event add a contribution record
625 //if primary participant contributing additional amount
626 //append (multiple participants) to its fee level. CRM-4196.
627 $isAdditionalAmount = FALSE;
628 if (count($params) > 1) {
629 $isAdditionalAmount = TRUE;
630 }
631
632 //passing contribution id is already registered.
633 $contribution = self::processContribution($this, $value, $result, $contactID, $pending, $isAdditionalAmount, $this->_paymentProcessor);
634 $value['contributionID'] = $contribution->id;
635 $value['contributionTypeID'] = $contribution->financial_type_id;
636 $value['receive_date'] = $contribution->receive_date;
637 $value['trxn_id'] = $contribution->trxn_id;
638 $value['contributionID'] = $contribution->id;
639 $value['contributionTypeID'] = $contribution->financial_type_id;
640 }
641 $value['contactID'] = $contactID;
642 $value['eventID'] = $this->_eventId;
643 $value['item_name'] = $value['description'];
644 }
645
646 if (!empty($value['contributionID'])) {
647 $this->_values['contributionId'] = $value['contributionID'];
648 }
649
650 //CRM-4453.
651 if (!empty($value['is_primary'])) {
652 $primaryCurrencyID = CRM_Utils_Array::value('currencyID', $value);
653 }
654 if (empty($value['currencyID'])) {
655 $value['currencyID'] = $primaryCurrencyID;
656 }
657
658 // CRM-11182 - Confirmation page might not be monetary
659 if ($this->_values['event']['is_monetary']) {
660 if (!$pending && !empty($value['is_primary']) &&
661 !$this->_allowWaitlist && !$this->_requireApproval
662 ) {
663 // transactionID & receive date required while building email template
664 $this->assign('trxn_id', CRM_Utils_Array::value('trxn_id', $value));
665 $this->assign('receive_date', CRM_Utils_Date::mysqlToIso(CRM_Utils_Array::value('receive_date', $value)));
666 $this->set('receiveDate', CRM_Utils_Date::mysqlToIso(CRM_Utils_Array::value('receive_date', $value)));
667 $this->set('trxnId', CRM_Utils_Array::value('trxn_id', $value));
668 }
669 }
670
671 $value['fee_amount'] = CRM_Utils_Array::value('amount', $value);
672 $this->set('value', $value);
673
674 // handle register date CRM-4320
675 if ($this->_allowConfirmation) {
676 $registerDate = CRM_Utils_Array::value('participant_register_date', $params);
677 }
678 elseif (!empty($params['participant_register_date']) &&
679 is_array($params['participant_register_date']) &&
680 !empty($params['participant_register_date'])
681 ) {
682 $registerDate = CRM_Utils_Date::format($params['participant_register_date']);
683 }
684 else {
685 $registerDate = date('YmdHis');
686 }
687 $this->assign('register_date', $registerDate);
688
689 $this->confirmPostProcess($contactID, $contribution, $payment);
690 }
691
692 //handle if no additional participant.
693 if (!$registerByID) {
694 $registerByID = $this->get('registerByID');
695 }
696
697 $this->set('participantIDs', $this->_participantIDS);
698
699 // create line items, CRM-5313
700 if ($this->_priceSetId &&
701 !empty($this->_lineItem)
702 ) {
703 // take all processed participant ids.
704 $allParticipantIds = $this->_participantIDS;
705
706 // when participant re-walk wizard.
707 if ($this->_allowConfirmation &&
708 !empty($this->_additionalParticipantIds)
709 ) {
710 $allParticipantIds = array_merge([$registerByID], $this->_additionalParticipantIds);
711 }
712
713 $entityTable = 'civicrm_participant';
714 $invoiceSettings = Civi::settings()->get('contribution_invoice_settings');
715 $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings);
716 $totalTaxAmount = 0;
717 $dataArray = [];
718 foreach ($this->_lineItem as $key => $value) {
719 if ($value == 'skip') {
720 continue;
721 }
722 if ($entityId = CRM_Utils_Array::value($key, $allParticipantIds)) {
723 // do cleanup line items if participant re-walking wizard.
724 if ($this->_allowConfirmation) {
725 CRM_Price_BAO_LineItem::deleteLineItems($entityId, $entityTable);
726 }
727 $lineItem[$this->_priceSetId] = $value;
728 CRM_Price_BAO_LineItem::processPriceSet($entityId, $lineItem, $contribution, $entityTable);
729 }
730 if ($invoicing) {
731 foreach ($value as $line) {
732 if (isset($line['tax_amount']) && isset($line['tax_rate'])) {
733 $totalTaxAmount = $line['tax_amount'] + $totalTaxAmount;
734 if (isset($dataArray[$line['tax_rate']])) {
735 $dataArray[$line['tax_rate']] = $dataArray[$line['tax_rate']] + CRM_Utils_Array::value('tax_amount', $line);
736 }
737 else {
738 $dataArray[$line['tax_rate']] = CRM_Utils_Array::value('tax_amount', $line);
739 }
740 }
741 }
742 }
743 }
744 if ($invoicing) {
745 $this->assign('dataArray', $dataArray);
746 $this->assign('totalTaxAmount', $totalTaxAmount);
747 }
748 }
749
750 //update status and send mail to cancelled additional participants, CRM-4320
751 if ($this->_allowConfirmation && is_array($cancelledIds) && !empty($cancelledIds)) {
752 $cancelledId = array_search('Cancelled',
753 CRM_Event_PseudoConstant::participantStatus(NULL, "class = 'Negative'")
754 );
755 CRM_Event_BAO_Participant::transitionParticipants($cancelledIds, $cancelledId);
756 }
757
758 $isTest = FALSE;
759 if ($this->_action & CRM_Core_Action::PREVIEW) {
760 $isTest = TRUE;
761 }
762
763 // for Transfer checkout.
764 // The concept of contributeMode is deprecated.
765 if (($this->_contributeMode == 'checkout' ||
766 $this->_contributeMode == 'notify'
767 ) && empty($params[0]['is_pay_later']) &&
768 !$this->_allowWaitlist && !$this->_requireApproval &&
769 $this->_totalAmount > 0
770 ) {
771
772 $primaryParticipant = $this->get('primaryParticipant');
773
774 if (empty($primaryParticipant['participantID'])) {
775 $primaryParticipant['participantID'] = $registerByID;
776 }
777
778 //build an array of custom profile and assigning it to template
779 $customProfile = CRM_Event_BAO_Event::buildCustomProfile($registerByID, $this->_values, NULL, $isTest);
780 if (count($customProfile)) {
781 $this->assign('customProfile', $customProfile);
782 $this->set('customProfile', $customProfile);
783 }
784
785 // do a transfer only if a monetary payment greater than 0
786 if ($this->_values['event']['is_monetary'] && $primaryParticipant) {
787 if ($payment && is_object($payment)) {
788 //CRM 14512 provide line items of all participants to payment gateway
789 $primaryContactId = $this->get('primaryContactId');
790
791 //build an array of cId/pId of participants
792 $additionalIDs = CRM_Event_BAO_Event::buildCustomProfile($registerByID, NULL, $primaryContactId, $isTest, TRUE);
793
794 //need to copy, since we are unsetting on the way.
795 $copyParticipantCountLines = $participantCount;
796
797 //lets carry all participant params w/ values.
798 foreach ($additionalIDs as $participantID => $contactId) {
799 $participantNum = NULL;
800 $participantNum = $participantID;
801 if ($participantID == $registerByID) {
802 // This is the is primary participant.
803 $participantNum = 0;
804 }
805 else {
806 if ($participantNum = array_search('participant', $copyParticipantCountLines)) {
807 //if no participant found break.
808 if ($participantNum === NULL) {
809 break;
810 }
811 //unset current participant so we don't check them again
812 unset($copyParticipantCountLines[$participantNum]);
813 }
814 }
815 // get values of line items
816 if ($this->_amount) {
817 $amount = [];
818 $amount[$participantNum]['label'] = preg_replace('/\ 1/', '', $params[$participantNum]['amount_level']);
819 $amount[$participantNum]['amount'] = $params[$participantNum]['amount'];
820 $params[$participantNum]['amounts'] = $amount;
821 }
822
823 if (!empty($this->_lineItem)) {
824 $lineItems = $this->_lineItem;
825 $lineItem = [];
826 if ($lineItemValue = CRM_Utils_Array::value($participantNum, $lineItems)) {
827 $lineItem[] = $lineItemValue;
828 }
829 $params[$participantNum]['lineItem'] = $lineItem;
830 }
831
832 //only add additional particpants and not the primary particpant as we already have that
833 //added to $primaryParticipant so that this change doesn't break or require changes to
834 //existing gateway implementations
835 $primaryParticipant['participants_info'][$participantID] = $params[$participantNum];
836 }
837
838 //get event custom field information
839 $groupTree = CRM_Core_BAO_CustomGroup::getTree('Event', NULL, $this->_eventId, 0, $this->_values['event']['event_type_id']);
840 $primaryParticipant['eventCustomFields'] = $groupTree;
841
842 // call postprocess hook before leaving
843 $this->postProcessHook();
844
845 $this->processPayment($payment, $primaryParticipant);
846 }
847 else {
848 CRM_Core_Error::fatal($paymentObjError);
849 }
850 }
851 }
852 else {
853 //otherwise send mail Confirmation/Receipt
854 $primaryContactId = $this->get('primaryContactId');
855
856 //build an array of cId/pId of participants
857 $additionalIDs = CRM_Event_BAO_Event::buildCustomProfile($registerByID,
858 NULL, $primaryContactId, $isTest,
859 TRUE
860 );
861 //let's send mails to all with meaningful text, CRM-4320.
862 $this->assign('isOnWaitlist', $this->_allowWaitlist);
863 $this->assign('isRequireApproval', $this->_requireApproval);
864
865 //need to copy, since we are unsetting on the way.
866 $copyParticipantCount = $participantCount;
867
868 //let's carry all participant params w/ values.
869 foreach ($additionalIDs as $participantID => $contactId) {
870 $participantNum = NULL;
871 if ($participantID == $registerByID) {
872 $participantNum = 0;
873 }
874 else {
875 if ($participantNum = array_search('participant', $copyParticipantCount)) {
876 unset($copyParticipantCount[$participantNum]);
877 }
878 }
879 if ($participantNum === NULL) {
880 break;
881 }
882
883 //carry the participant submitted values.
884 $this->_values['params'][$participantID] = $params[$participantNum];
885 }
886
887 foreach ($additionalIDs as $participantID => $contactId) {
888 $participantNum = 0;
889 if ($participantID == $registerByID) {
890 //set as Primary Participant
891 $this->assign('isPrimary', 1);
892 //build an array of custom profile and assigning it to template.
893 $customProfile = CRM_Event_BAO_Event::buildCustomProfile($participantID, $this->_values, NULL, $isTest);
894
895 if (count($customProfile)) {
896 $this->assign('customProfile', $customProfile);
897 $this->set('customProfile', $customProfile);
898 }
899 $this->_values['params']['additionalParticipant'] = FALSE;
900 }
901 else {
902 //take the Additional participant number.
903 if ($participantNum = array_search('participant', $participantCount)) {
904 unset($participantCount[$participantNum]);
905 }
906 // Change $this->_values['participant'] to include additional participant values
907 $ids = $participantValues = [];
908 $participantParams = ['id' => $participantID];
909 CRM_Event_BAO_Participant::getValues($participantParams, $participantValues, $ids);
910 $this->_values['participant'] = $participantValues[$participantID];
911
912 $this->assign('isPrimary', 0);
913 $this->assign('customProfile', NULL);
914 //Additional Participant should get only it's payment information
915 if (!empty($this->_amount)) {
916 $amount = [];
917 $params = $this->get('params');
918 $amount[$participantNum]['label'] = preg_replace('/\ 1/', '', $params[$participantNum]['amount_level']);
919 $amount[$participantNum]['amount'] = $params[$participantNum]['amount'];
920 $this->assign('amounts', $amount);
921 }
922 if ($this->_lineItem) {
923 $lineItems = $this->_lineItem;
924 $lineItem = [];
925 if ($lineItemValue = CRM_Utils_Array::value($participantNum, $lineItems)) {
926 $lineItem[] = $lineItemValue;
927 }
928 if ($invoicing) {
929 $individual = $this->get('individual');
930 $dataArray[key($dataArray)] = $individual[$participantNum]['totalTaxAmt'];
931 $this->assign('dataArray', $dataArray);
932 $this->assign('totalAmount', $individual[$participantNum]['totalAmtWithTax']);
933 $this->assign('totalTaxAmount', $individual[$participantNum]['totalTaxAmt']);
934 $this->assign('individual', [$individual[$participantNum]]);
935 }
936 $this->assign('lineItem', $lineItem);
937 }
938 $this->_values['params']['additionalParticipant'] = TRUE;
939 $this->assign('isAdditionalParticipant', $this->_values['params']['additionalParticipant']);
940 }
941
942 //pass these variables since these are run time calculated.
943 $this->_values['params']['isOnWaitlist'] = $this->_allowWaitlist;
944 $this->_values['params']['isRequireApproval'] = $this->_requireApproval;
945
946 //send mail to primary as well as additional participants.
947 $this->assign('contactID', $contactId);
948 $this->assign('participantID', $participantID);
949 CRM_Event_BAO_Event::sendMail($contactId, $this->_values, $participantID, $isTest);
950 }
951 }
952 }
953
954 /**
955 * Process the contribution.
956 *
957 * @param CRM_Core_Form $form
958 * @param array $params
959 * @param array $result
960 * @param int $contactID
961 * @param bool $pending
962 * @param bool $isAdditionalAmount
963 * @param array $paymentProcessor
964 *
965 * @return \CRM_Contribute_BAO_Contribution
966 * @throws \CRM_Core_Exception
967 */
968 public static function processContribution(
969 &$form, $params, $result, $contactID,
970 $pending = FALSE, $isAdditionalAmount = FALSE,
971 $paymentProcessor = NULL
972 ) {
973 $transaction = new CRM_Core_Transaction();
974
975 $now = date('YmdHis');
976 $receiptDate = NULL;
977
978 if (!empty($form->_values['event']['is_email_confirm'])) {
979 $receiptDate = $now;
980 }
981 //CRM-4196
982 if ($isAdditionalAmount) {
983 $params['amount_level'] = $params['amount_level'] . ts(' (multiple participants)') . CRM_Core_DAO::VALUE_SEPARATOR;
984 }
985
986 // CRM-20264: fetch CC type ID and number (last 4 digit) and assign it back to $params
987 CRM_Contribute_Form_AbstractEditPayment::formatCreditCardDetails($params);
988
989 $contribParams = [
990 'contact_id' => $contactID,
991 'financial_type_id' => !empty($form->_values['event']['financial_type_id']) ? $form->_values['event']['financial_type_id'] : $params['financial_type_id'],
992 'receive_date' => $now,
993 'total_amount' => $params['amount'],
994 'tax_amount' => $params['tax_amount'],
995 'amount_level' => $params['amount_level'],
996 'invoice_id' => $params['invoiceID'],
997 'currency' => $params['currencyID'],
998 'source' => !empty($params['participant_source']) ? $params['participant_source'] : $params['description'],
999 'is_pay_later' => CRM_Utils_Array::value('is_pay_later', $params, 0),
1000 'campaign_id' => CRM_Utils_Array::value('campaign_id', $params),
1001 'card_type_id' => CRM_Utils_Array::value('card_type_id', $params),
1002 'pan_truncation' => CRM_Utils_Array::value('pan_truncation', $params),
1003 ];
1004
1005 if ($paymentProcessor) {
1006 $contribParams['payment_instrument_id'] = $paymentProcessor['payment_instrument_id'];
1007 $contribParams['payment_processor'] = $paymentProcessor['id'];
1008 }
1009
1010 if (!$pending && $result) {
1011 $contribParams += [
1012 'fee_amount' => CRM_Utils_Array::value('fee_amount', $result),
1013 'net_amount' => CRM_Utils_Array::value('net_amount', $result, $params['amount']),
1014 'trxn_id' => $result['trxn_id'],
1015 'receipt_date' => $receiptDate,
1016 ];
1017 }
1018
1019 $allStatuses = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
1020 $contribParams['contribution_status_id'] = array_search('Completed', $allStatuses);
1021 if ($pending) {
1022 $contribParams['contribution_status_id'] = array_search('Pending', $allStatuses);
1023 }
1024
1025 $contribParams['is_test'] = 0;
1026 if ($form->_action & CRM_Core_Action::PREVIEW || CRM_Utils_Array::value('mode', $params) == 'test') {
1027 $contribParams['is_test'] = 1;
1028 }
1029
1030 if (!empty($contribParams['invoice_id'])) {
1031 $contribParams['id'] = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution',
1032 $contribParams['invoice_id'],
1033 'id',
1034 'invoice_id'
1035 );
1036 }
1037
1038 if (Civi::settings()->get('deferred_revenue_enabled')) {
1039 $eventStartDate = CRM_Utils_Array::value(
1040 'start_date',
1041 CRM_Utils_Array::value(
1042 'event',
1043 $form->_values
1044 )
1045 );
1046 if (strtotime($eventStartDate) > strtotime(date('Ymt'))) {
1047 $contribParams['revenue_recognition_date'] = date('Ymd', strtotime($eventStartDate));
1048 }
1049 }
1050 //create an contribution address
1051 // The concept of contributeMode is deprecated. Elsewhere we use the function processBillingAddress() - although
1052 // currently that is only inherited by back-office forms.
1053 if ($form->_contributeMode != 'notify' && empty($params['is_pay_later'])) {
1054 $contribParams['address_id'] = CRM_Contribute_BAO_Contribution::createAddress($params, $form->_bltID);
1055 }
1056
1057 $contribParams['skipLineItem'] = 1;
1058 $contribParams['skipCleanMoney'] = 1;
1059 // create contribution record
1060 $contribution = CRM_Contribute_BAO_Contribution::add($contribParams);
1061 // CRM-11124
1062 CRM_Event_BAO_Participant::createDiscountTrxn($form->_eventId, $contribParams, NULL, CRM_Price_BAO_PriceSet::parseFirstPriceSetValueIDFromParams($params));
1063
1064 // process soft credit / pcp pages
1065 if (!empty($params['pcp_made_through_id'])) {
1066 CRM_Contribute_BAO_ContributionSoft::formatSoftCreditParams($params, $form);
1067 CRM_Contribute_BAO_ContributionSoft::processSoftContribution($params, $contribution);
1068 }
1069
1070 $transaction->commit();
1071
1072 return $contribution;
1073 }
1074
1075 /**
1076 * Fix the Location Fields.
1077 *
1078 * @todo Reconcile with the contribution method formatParamsForPaymentProcessor
1079 * rather than adding different logic to check when to keep the billing
1080 * fields. There might be a difference in handling guest/multiple
1081 * participants though.
1082 *
1083 * @param array $params
1084 * @param array $fields
1085 * @param CRM_Core_Form $form
1086 */
1087 public static function fixLocationFields(&$params, &$fields, &$form) {
1088 if (!empty($form->_fields)) {
1089 foreach ($form->_fields as $name => $dontCare) {
1090 $fields[$name] = 1;
1091 }
1092 }
1093
1094 // If there's no 'first_name' in the profile then overwrite the names from
1095 // the billing fields (if they are set)
1096 if (is_array($fields)) {
1097 if (!array_key_exists('first_name', $fields)) {
1098 $nameFields = ['first_name', 'middle_name', 'last_name'];
1099 foreach ($nameFields as $name) {
1100 $fields[$name] = 1;
1101 if (array_key_exists("billing_$name", $params)) {
1102 $params[$name] = $params["billing_{$name}"];
1103 $params['preserveDBName'] = TRUE;
1104 }
1105 }
1106 }
1107 }
1108
1109 // Add the billing names to the billing address, if a billing name is set
1110 if (!empty($params['billing_first_name'])) {
1111 $params["address_name-{$form->_bltID}"] = CRM_Utils_Array::value('billing_first_name', $params) . ' ' . CRM_Utils_Array::value('billing_middle_name', $params) . ' ' . CRM_Utils_Array::value('billing_last_name', $params);
1112 $fields["address_name-{$form->_bltID}"] = 1;
1113 }
1114
1115 $fields["email-{$form->_bltID}"] = 1;
1116 $fields['email-Primary'] = 1;
1117
1118 //if its pay later or additional participant set email address as primary.
1119 if ((!empty($params['is_pay_later']) || empty($params['is_primary']) ||
1120 !$form->_values['event']['is_monetary'] ||
1121 $form->_allowWaitlist ||
1122 $form->_requireApproval
1123 ) && !empty($params["email-{$form->_bltID}"])
1124 ) {
1125 $params['email-Primary'] = $params["email-{$form->_bltID}"];
1126 }
1127 }
1128
1129 /**
1130 * Update contact fields.
1131 *
1132 * @param int $contactID
1133 * @param array $params
1134 * @param array $fields
1135 * @param CRM_Core_Form $form
1136 *
1137 * @return int
1138 */
1139 public static function updateContactFields($contactID, $params, $fields, &$form) {
1140 //add the contact to group, if add to group is selected for a
1141 //particular uf group
1142
1143 // get the add to groups
1144 $addToGroups = [];
1145
1146 if (!empty($form->_fields)) {
1147 foreach ($form->_fields as $key => $value) {
1148 if (!empty($value['add_to_group_id'])) {
1149 $addToGroups[$value['add_to_group_id']] = $value['add_to_group_id'];
1150 }
1151 }
1152 }
1153
1154 // check for profile double opt-in and get groups to be subscribed
1155 $subscribeGroupIds = CRM_Core_BAO_UFGroup::getDoubleOptInGroupIds($params, $contactID);
1156
1157 foreach ($addToGroups as $k) {
1158 if (array_key_exists($k, $subscribeGroupIds)) {
1159 unset($addToGroups[$k]);
1160 }
1161 }
1162
1163 // since we are directly adding contact to group lets unset it from mailing
1164 if (!empty($addToGroups)) {
1165 foreach ($addToGroups as $groupId) {
1166 if (isset($subscribeGroupIds[$groupId])) {
1167 unset($subscribeGroupIds[$groupId]);
1168 }
1169 }
1170 }
1171 if ($contactID) {
1172 $ctype = CRM_Core_DAO::getFieldValue(
1173 'CRM_Contact_DAO_Contact',
1174 $contactID,
1175 'contact_type'
1176 );
1177
1178 if (array_key_exists('contact_id', $params) && empty($params['contact_id'])) {
1179 // we unset this here because the downstream function ignores the contactID we give it
1180 // if it is set & it is difficult to understand the implications of 'fixing' this downstream
1181 // but if we are passing a contact id into this function it's reasonable to assume we don't
1182 // want it ignored
1183 unset($params['contact_id']);
1184 }
1185
1186 $contactID = CRM_Contact_BAO_Contact::createProfileContact(
1187 $params,
1188 $fields,
1189 $contactID,
1190 $addToGroups,
1191 NULL,
1192 $ctype,
1193 TRUE
1194 );
1195 }
1196 else {
1197
1198 foreach (CRM_Contact_BAO_Contact::$_greetingTypes as $greeting) {
1199 if (!isset($params[$greeting . '_id'])) {
1200 $params[$greeting . '_id'] = CRM_Contact_BAO_Contact_Utils::defaultGreeting('Individual', $greeting);
1201 }
1202 }
1203
1204 $contactID = CRM_Contact_BAO_Contact::createProfileContact($params,
1205 $fields,
1206 NULL,
1207 $addToGroups,
1208 NULL,
1209 NULL,
1210 TRUE
1211 );
1212 $form->set('contactID', $contactID);
1213 }
1214
1215 //get email primary first if exist
1216 $subscribtionEmail = ['email' => CRM_Utils_Array::value('email-Primary', $params)];
1217 if (!$subscribtionEmail['email']) {
1218 $subscribtionEmail['email'] = CRM_Utils_Array::value("email-{$form->_bltID}", $params);
1219 }
1220 // subscribing contact to groups
1221 if (!empty($subscribeGroupIds) && $subscribtionEmail['email']) {
1222 CRM_Mailing_Event_BAO_Subscribe::commonSubscribe($subscribeGroupIds, $subscribtionEmail, $contactID);
1223 }
1224
1225 return $contactID;
1226 }
1227
1228 /**
1229 * Assign Profiles.
1230 *
1231 * @param CRM_Core_Form $form
1232 */
1233 public static function assignProfiles(&$form) {
1234 $participantParams = $form->_params;
1235 $formattedValues = $profileFields = [];
1236 $count = 1;
1237 foreach ($participantParams as $participantNum => $participantValue) {
1238 if ($participantNum) {
1239 $prefix1 = 'additional';
1240 $prefix2 = 'additional_';
1241 }
1242 else {
1243 $prefix1 = '';
1244 $prefix2 = '';
1245 }
1246 if ($participantValue != 'skip') {
1247 //get the customPre profile info
1248 if (!empty($form->_values[$prefix2 . 'custom_pre_id'])) {
1249 $values = $groupName = [];
1250 CRM_Event_BAO_Event::displayProfile($participantValue,
1251 $form->_values[$prefix2 . 'custom_pre_id'],
1252 $groupName,
1253 $values,
1254 $profileFields
1255 );
1256
1257 if (count($values)) {
1258 $formattedValues[$count][$prefix1 . 'CustomPre'] = $values;
1259 }
1260 $formattedValues[$count][$prefix1 . 'CustomPreGroupTitle'] = CRM_Utils_Array::value('groupTitle', $groupName);
1261 }
1262 //get the customPost profile info
1263 if (!empty($form->_values[$prefix2 . 'custom_post_id'])) {
1264 $values = $groupName = [];
1265 foreach ($form->_values[$prefix2 . 'custom_post_id'] as $gids) {
1266 $val = [];
1267 CRM_Event_BAO_Event::displayProfile($participantValue,
1268 $gids,
1269 $group,
1270 $val,
1271 $profileFields
1272 );
1273 $values[$gids] = $val;
1274 $groupName[$gids] = $group;
1275 }
1276
1277 if (count($values)) {
1278 $formattedValues[$count][$prefix1 . 'CustomPost'] = $values;
1279 }
1280
1281 if (isset($formattedValues[$count][$prefix1 . 'CustomPre'])) {
1282 $formattedValues[$count][$prefix1 . 'CustomPost'] = array_diff_assoc($formattedValues[$count][$prefix1 . 'CustomPost'],
1283 $formattedValues[$count][$prefix1 . 'CustomPre']
1284 );
1285 }
1286
1287 $formattedValues[$count][$prefix1 . 'CustomPostGroupTitle'] = $groupName;
1288 }
1289 $count++;
1290 }
1291 $form->_fields = $profileFields;
1292 }
1293 if (!empty($formattedValues)) {
1294 $form->assign('primaryParticipantProfile', $formattedValues[1]);
1295 $form->set('primaryParticipantProfile', $formattedValues[1]);
1296 if ($count > 2) {
1297 unset($formattedValues[1]);
1298 $form->assign('addParticipantProfile', $formattedValues);
1299 $form->set('addParticipantProfile', $formattedValues);
1300 }
1301 }
1302 }
1303
1304 /**
1305 * Submit in test mode.
1306 *
1307 * @param $params
1308 */
1309 public static function testSubmit($params) {
1310 $form = new CRM_Event_Form_Registration_Confirm();
1311 // This way the mocked up controller ignores the session stuff.
1312 $_SERVER['REQUEST_METHOD'] = 'GET';
1313 $_REQUEST['id'] = $form->_eventId = $params['id'];
1314 $form->controller = new CRM_Event_Controller_Registration();
1315 $form->_params = $params['params'];
1316 // This happens in buildQuickForm so emulate here.
1317 $form->_amount = $form->_totalAmount = CRM_Utils_Rule::cleanMoney(CRM_Utils_Array::value('totalAmount', $params));
1318 $form->set('params', $params['params']);
1319 $form->_values['custom_pre_id'] = [];
1320 $form->_values['custom_post_id'] = [];
1321 $form->_values['event'] = CRM_Utils_Array::value('event', $params);
1322 $form->_contributeMode = $params['contributeMode'];
1323 $eventParams = ['id' => $params['id']];
1324 CRM_Event_BAO_Event::retrieve($eventParams, $form->_values['event']);
1325 $form->set('registerByID', $params['registerByID']);
1326 if (!empty($params['paymentProcessorObj'])) {
1327 $form->_paymentProcessor = $params['paymentProcessorObj'];
1328 }
1329 $form->postProcess();
1330 }
1331
1332 /**
1333 * Process the payment, redirecting back to the page on error.
1334 *
1335 * @param $payment
1336 * @param $value
1337 *
1338 * @return array
1339 */
1340 private function processPayment($payment, $value) {
1341 try {
1342 $result = $payment->doPayment($value, 'event');
1343 return [$result, $value];
1344 }
1345 catch (\Civi\Payment\Exception\PaymentProcessorException $e) {
1346 Civi::log()->error('Payment processor exception: ' . $e->getMessage());
1347 CRM_Core_Session::singleton()->setStatus($e->getMessage());
1348 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/event/register', "id={$this->_eventId}"));
1349 }
1350 return [];
1351 }
1352
1353 /**
1354 * Clean money fields from the form.
1355 *
1356 * @param array $params
1357 */
1358 protected function cleanMoneyFields(&$params) {
1359 foreach ($this->submittableMoneyFields as $moneyField) {
1360 foreach ($params as $index => $paramField) {
1361 if (isset($paramField[$moneyField])) {
1362 $params[$index][$moneyField] = CRM_Utils_Rule::cleanMoney($paramField[$moneyField]);
1363 }
1364 }
1365 }
1366 }
1367
1368 }